mirror of https://github.com/FongMi/TV.git
parent
a78261751e
commit
5250bbc99d
@ -1,2 +1,2 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest package="tv.danmaku.ijk.media.player" /> |
||||
<manifest package="com.lodz.android.mmsplayer" /> |
||||
@ -0,0 +1,10 @@ |
||||
package com.lodz.android.mmsplayer.ijk.bean; |
||||
|
||||
public class MediaInfo { |
||||
|
||||
public String playerName = ""; |
||||
public String resolution = ""; |
||||
public String length = ""; |
||||
public TrackVideoInfo trackVideoInfoBean; |
||||
public TrackAudioInfo trackAudioInfoBean; |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
package com.lodz.android.mmsplayer.ijk.bean; |
||||
|
||||
public class TrackAudioInfo { |
||||
|
||||
public String type = ""; |
||||
public String language = ""; |
||||
public String codec = ""; |
||||
public String profileLevel = ""; |
||||
public String sampleRate = ""; |
||||
public String channels = ""; |
||||
public String bitRate = ""; |
||||
public boolean isSelected; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package com.lodz.android.mmsplayer.ijk.bean; |
||||
|
||||
public class TrackVideoInfo { |
||||
|
||||
public String type = ""; |
||||
public String language = ""; |
||||
public String codec = ""; |
||||
public String profileLevel = ""; |
||||
public String pixelFormat = ""; |
||||
public String resolution = ""; |
||||
public String frameRate = ""; |
||||
public String bitRate = ""; |
||||
public boolean isSelected; |
||||
} |
||||
@ -0,0 +1,73 @@ |
||||
package com.lodz.android.mmsplayer.ijk.media; |
||||
|
||||
import android.graphics.SurfaceTexture; |
||||
import android.view.Surface; |
||||
import android.view.SurfaceHolder; |
||||
import android.view.View; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
||||
|
||||
public interface IRenderView { |
||||
|
||||
int AR_ASPECT_FIT_PARENT = 0; |
||||
int AR_ASPECT_FILL_PARENT = 1; |
||||
int AR_ASPECT_WRAP_CONTENT = 2; |
||||
int AR_MATCH_PARENT = 3; |
||||
int AR_16_9_FIT_PARENT = 4; |
||||
int AR_4_3_FIT_PARENT = 5; |
||||
|
||||
View getView(); |
||||
|
||||
boolean shouldWaitForResize(); |
||||
|
||||
void setVideoSize(int videoWidth, int videoHeight); |
||||
|
||||
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen); |
||||
|
||||
void setVideoRotation(int degree); |
||||
|
||||
void setAspectRatio(int aspectRatio); |
||||
|
||||
void addRenderCallback(@NonNull IRenderCallback callback); |
||||
|
||||
void removeRenderCallback(@NonNull IRenderCallback callback); |
||||
|
||||
interface ISurfaceHolder { |
||||
|
||||
void bindToMediaPlayer(IMediaPlayer mp); |
||||
|
||||
@NonNull |
||||
IRenderView getRenderView(); |
||||
|
||||
@Nullable |
||||
SurfaceHolder getSurfaceHolder(); |
||||
|
||||
@Nullable |
||||
Surface openSurface(); |
||||
|
||||
@Nullable |
||||
SurfaceTexture getSurfaceTexture(); |
||||
} |
||||
|
||||
interface IRenderCallback { |
||||
/** |
||||
* @param holder |
||||
* @param width could be 0 |
||||
* @param height could be 0 |
||||
*/ |
||||
void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height); |
||||
|
||||
/** |
||||
* @param holder |
||||
* @param format could be 0 |
||||
* @param width |
||||
* @param height |
||||
*/ |
||||
void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height); |
||||
|
||||
void onSurfaceDestroyed(@NonNull ISurfaceHolder holder); |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,162 @@ |
||||
package com.lodz.android.mmsplayer.ijk.media; |
||||
|
||||
import android.view.View; |
||||
|
||||
import java.lang.ref.WeakReference; |
||||
|
||||
public final class MeasureHelper { |
||||
|
||||
private final WeakReference<View> mWeakView; |
||||
|
||||
private int mVideoWidth; |
||||
private int mVideoHeight; |
||||
private int mVideoSarNum; |
||||
private int mVideoSarDen; |
||||
|
||||
private int mVideoRotationDegree; |
||||
|
||||
private int mMeasuredWidth; |
||||
private int mMeasuredHeight; |
||||
|
||||
private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT; |
||||
|
||||
public MeasureHelper(View view) { |
||||
mWeakView = new WeakReference<>(view); |
||||
} |
||||
|
||||
public View getView() { |
||||
return mWeakView.get(); |
||||
} |
||||
|
||||
public void setVideoSize(int videoWidth, int videoHeight) { |
||||
mVideoWidth = videoWidth; |
||||
mVideoHeight = videoHeight; |
||||
} |
||||
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
||||
mVideoSarNum = videoSarNum; |
||||
mVideoSarDen = videoSarDen; |
||||
} |
||||
|
||||
public void setVideoRotation(int videoRotationDegree) { |
||||
mVideoRotationDegree = videoRotationDegree; |
||||
} |
||||
|
||||
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) { |
||||
int tempSpec = widthMeasureSpec; |
||||
widthMeasureSpec = heightMeasureSpec; |
||||
heightMeasureSpec = tempSpec; |
||||
} |
||||
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec); |
||||
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec); |
||||
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) { |
||||
width = widthMeasureSpec; |
||||
height = heightMeasureSpec; |
||||
} else if (mVideoWidth > 0 && mVideoHeight > 0) { |
||||
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec); |
||||
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec); |
||||
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec); |
||||
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec); |
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) { |
||||
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize; |
||||
float displayAspectRatio; |
||||
switch (mCurrentAspectRatio) { |
||||
case IRenderView.AR_16_9_FIT_PARENT: |
||||
displayAspectRatio = 16.0f / 9.0f; |
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) displayAspectRatio = 1.0f / displayAspectRatio; |
||||
break; |
||||
case IRenderView.AR_4_3_FIT_PARENT: |
||||
displayAspectRatio = 4.0f / 3.0f; |
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) displayAspectRatio = 1.0f / displayAspectRatio; |
||||
break; |
||||
case IRenderView.AR_ASPECT_FIT_PARENT: |
||||
case IRenderView.AR_ASPECT_FILL_PARENT: |
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT: |
||||
default: |
||||
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight; |
||||
if (mVideoSarNum > 0 && mVideoSarDen > 0) displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen; |
||||
break; |
||||
} |
||||
boolean shouldBeWider = displayAspectRatio > specAspectRatio; |
||||
switch (mCurrentAspectRatio) { |
||||
case IRenderView.AR_ASPECT_FIT_PARENT: |
||||
case IRenderView.AR_16_9_FIT_PARENT: |
||||
case IRenderView.AR_4_3_FIT_PARENT: |
||||
if (shouldBeWider) { |
||||
width = widthSpecSize; |
||||
height = (int) (width / displayAspectRatio); |
||||
} else { |
||||
height = heightSpecSize; |
||||
width = (int) (height * displayAspectRatio); |
||||
} |
||||
break; |
||||
case IRenderView.AR_ASPECT_FILL_PARENT: |
||||
if (shouldBeWider) { |
||||
height = heightSpecSize; |
||||
width = (int) (height * displayAspectRatio); |
||||
} else { |
||||
width = widthSpecSize; |
||||
height = (int) (width / displayAspectRatio); |
||||
} |
||||
break; |
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT: |
||||
default: |
||||
if (shouldBeWider) { |
||||
width = Math.min(mVideoWidth, widthSpecSize); |
||||
height = (int) (width / displayAspectRatio); |
||||
} else { |
||||
height = Math.min(mVideoHeight, heightSpecSize); |
||||
width = (int) (height * displayAspectRatio); |
||||
} |
||||
break; |
||||
} |
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) { |
||||
width = widthSpecSize; |
||||
height = heightSpecSize; |
||||
if (mVideoWidth * height < width * mVideoHeight) { |
||||
width = height * mVideoWidth / mVideoHeight; |
||||
} else if (mVideoWidth * height > width * mVideoHeight) { |
||||
height = width * mVideoHeight / mVideoWidth; |
||||
} |
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY) { |
||||
width = widthSpecSize; |
||||
height = width * mVideoHeight / mVideoWidth; |
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { |
||||
height = heightSpecSize; |
||||
} |
||||
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) { |
||||
height = heightSpecSize; |
||||
width = height * mVideoWidth / mVideoHeight; |
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { |
||||
width = widthSpecSize; |
||||
} |
||||
} else { |
||||
width = mVideoWidth; |
||||
height = mVideoHeight; |
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { |
||||
height = heightSpecSize; |
||||
width = height * mVideoWidth / mVideoHeight; |
||||
} |
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { |
||||
width = widthSpecSize; |
||||
height = width * mVideoHeight / mVideoWidth; |
||||
} |
||||
} |
||||
} |
||||
mMeasuredWidth = width; |
||||
mMeasuredHeight = height; |
||||
} |
||||
|
||||
public int getMeasuredWidth() { |
||||
return mMeasuredWidth; |
||||
} |
||||
|
||||
public int getMeasuredHeight() { |
||||
return mMeasuredHeight; |
||||
} |
||||
|
||||
public void setAspectRatio(int aspectRatio) { |
||||
mCurrentAspectRatio = aspectRatio; |
||||
} |
||||
} |
||||
@ -0,0 +1,56 @@ |
||||
package com.lodz.android.mmsplayer.ijk.media; |
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
||||
import tv.danmaku.ijk.media.player.MediaPlayerProxy; |
||||
import tv.danmaku.ijk.media.player.TextureMediaPlayer; |
||||
|
||||
public class MediaPlayerCompat { |
||||
|
||||
public static String getName(IMediaPlayer mp) { |
||||
if (mp == null) { |
||||
return "null"; |
||||
} else if (mp instanceof TextureMediaPlayer) { |
||||
StringBuilder sb = new StringBuilder("TextureMediaPlayer <"); |
||||
IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer(); |
||||
if (internalMediaPlayer == null) { |
||||
sb.append("null>"); |
||||
} else { |
||||
sb.append(internalMediaPlayer.getClass().getSimpleName()); |
||||
sb.append(">"); |
||||
} |
||||
return sb.toString(); |
||||
} else { |
||||
return mp.getClass().getSimpleName(); |
||||
} |
||||
} |
||||
|
||||
public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) { |
||||
IjkMediaPlayer ijkMediaPlayer = null; |
||||
if (mp == null) return null; |
||||
if (mp instanceof IjkMediaPlayer) { |
||||
ijkMediaPlayer = (IjkMediaPlayer) mp; |
||||
} else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) { |
||||
ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer(); |
||||
} |
||||
return ijkMediaPlayer; |
||||
} |
||||
|
||||
public static void selectTrack(IMediaPlayer mp, int stream) { |
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
||||
if (ijkMediaPlayer == null) return; |
||||
ijkMediaPlayer.selectTrack(stream); |
||||
} |
||||
|
||||
public static void deselectTrack(IMediaPlayer mp, int stream) { |
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
||||
if (ijkMediaPlayer == null) return; |
||||
ijkMediaPlayer.deselectTrack(stream); |
||||
} |
||||
|
||||
public static int getSelectedTrack(IMediaPlayer mp, int trackType) { |
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
||||
if (ijkMediaPlayer == null) return -1; |
||||
return ijkMediaPlayer.getSelectedTrack(trackType); |
||||
} |
||||
} |
||||
@ -0,0 +1,240 @@ |
||||
package com.lodz.android.mmsplayer.ijk.media; |
||||
|
||||
import android.content.Context; |
||||
import android.graphics.SurfaceTexture; |
||||
import android.util.AttributeSet; |
||||
import android.util.Log; |
||||
import android.view.Surface; |
||||
import android.view.SurfaceHolder; |
||||
import android.view.SurfaceView; |
||||
import android.view.View; |
||||
import android.view.accessibility.AccessibilityEvent; |
||||
import android.view.accessibility.AccessibilityNodeInfo; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
||||
|
||||
import java.lang.ref.WeakReference; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; |
||||
|
||||
public class SurfaceRenderView extends SurfaceView implements IRenderView { |
||||
|
||||
private MeasureHelper mMeasureHelper; |
||||
|
||||
public SurfaceRenderView(Context context) { |
||||
super(context); |
||||
initView(context); |
||||
} |
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs) { |
||||
super(context, attrs); |
||||
initView(context); |
||||
} |
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||
super(context, attrs, defStyleAttr); |
||||
initView(context); |
||||
} |
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
||||
super(context, attrs, defStyleAttr, defStyleRes); |
||||
initView(context); |
||||
} |
||||
|
||||
private void initView(Context context) { |
||||
mMeasureHelper = new MeasureHelper(this); |
||||
mSurfaceCallback = new SurfaceCallback(this); |
||||
getHolder().addCallback(mSurfaceCallback); |
||||
getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL); |
||||
} |
||||
|
||||
@Override |
||||
public View getView() { |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldWaitForResize() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoSize(int videoWidth, int videoHeight) { |
||||
if (videoWidth > 0 && videoHeight > 0) { |
||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight); |
||||
getHolder().setFixedSize(videoWidth, videoHeight); |
||||
requestLayout(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
||||
if (videoSarNum > 0 && videoSarDen > 0) { |
||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); |
||||
requestLayout(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoRotation(int degree) { |
||||
Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n"); |
||||
} |
||||
|
||||
@Override |
||||
public void setAspectRatio(int aspectRatio) { |
||||
mMeasureHelper.setAspectRatio(aspectRatio); |
||||
requestLayout(); |
||||
} |
||||
|
||||
@Override |
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); |
||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); |
||||
} |
||||
|
||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { |
||||
|
||||
private final SurfaceRenderView mSurfaceView; |
||||
private final SurfaceHolder mSurfaceHolder; |
||||
|
||||
private InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView, @Nullable SurfaceHolder surfaceHolder) { |
||||
mSurfaceView = surfaceView; |
||||
mSurfaceHolder = surfaceHolder; |
||||
} |
||||
|
||||
public void bindToMediaPlayer(IMediaPlayer mp) { |
||||
if (mp != null) { |
||||
if (mp instanceof ISurfaceTextureHolder) { |
||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; |
||||
textureHolder.setSurfaceTexture(null); |
||||
} |
||||
mp.setDisplay(mSurfaceHolder); |
||||
} |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public IRenderView getRenderView() { |
||||
return mSurfaceView; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public SurfaceHolder getSurfaceHolder() { |
||||
return mSurfaceHolder; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public SurfaceTexture getSurfaceTexture() { |
||||
return null; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Surface openSurface() { |
||||
if (mSurfaceHolder == null) return null; |
||||
return mSurfaceHolder.getSurface(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void addRenderCallback(@NonNull IRenderCallback callback) { |
||||
mSurfaceCallback.addRenderCallback(callback); |
||||
} |
||||
|
||||
@Override |
||||
public void removeRenderCallback(@NonNull IRenderCallback callback) { |
||||
mSurfaceCallback.removeRenderCallback(callback); |
||||
} |
||||
|
||||
private SurfaceCallback mSurfaceCallback; |
||||
|
||||
private static final class SurfaceCallback implements SurfaceHolder.Callback { |
||||
|
||||
private SurfaceHolder mSurfaceHolder; |
||||
private boolean mIsFormatChanged; |
||||
private int mFormat; |
||||
private int mWidth; |
||||
private int mHeight; |
||||
|
||||
private final WeakReference<SurfaceRenderView> mWeakSurfaceView; |
||||
private final Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<>(); |
||||
|
||||
private SurfaceCallback(@NonNull SurfaceRenderView surfaceView) { |
||||
mWeakSurfaceView = new WeakReference<>(surfaceView); |
||||
} |
||||
|
||||
private void addRenderCallback(@NonNull IRenderCallback callback) { |
||||
mRenderCallbackMap.put(callback, callback); |
||||
ISurfaceHolder surfaceHolder = null; |
||||
if (mSurfaceHolder != null) { |
||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); |
||||
} |
||||
if (mIsFormatChanged) { |
||||
if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
||||
callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight); |
||||
} |
||||
} |
||||
|
||||
private void removeRenderCallback(@NonNull IRenderCallback callback) { |
||||
mRenderCallbackMap.remove(callback); |
||||
} |
||||
|
||||
@Override |
||||
public void surfaceCreated(SurfaceHolder holder) { |
||||
mSurfaceHolder = holder; |
||||
mIsFormatChanged = false; |
||||
mFormat = 0; |
||||
mWidth = 0; |
||||
mHeight = 0; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void surfaceDestroyed(SurfaceHolder holder) { |
||||
mSurfaceHolder = null; |
||||
mIsFormatChanged = false; |
||||
mFormat = 0; |
||||
mWidth = 0; |
||||
mHeight = 0; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
||||
renderCallback.onSurfaceDestroyed(surfaceHolder); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
||||
mSurfaceHolder = holder; |
||||
mIsFormatChanged = true; |
||||
mFormat = format; |
||||
mWidth = width; |
||||
mHeight = height; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
||||
renderCallback.onSurfaceChanged(surfaceHolder, format, width, height); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) { |
||||
super.onInitializeAccessibilityEvent(event); |
||||
event.setClassName(SurfaceRenderView.class.getName()); |
||||
} |
||||
|
||||
@Override |
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { |
||||
super.onInitializeAccessibilityNodeInfo(info); |
||||
info.setClassName(SurfaceRenderView.class.getName()); |
||||
} |
||||
} |
||||
@ -0,0 +1,292 @@ |
||||
package com.lodz.android.mmsplayer.ijk.media; |
||||
|
||||
import android.content.Context; |
||||
import android.graphics.SurfaceTexture; |
||||
import android.util.AttributeSet; |
||||
import android.view.Surface; |
||||
import android.view.SurfaceHolder; |
||||
import android.view.TextureView; |
||||
import android.view.View; |
||||
import android.view.accessibility.AccessibilityEvent; |
||||
import android.view.accessibility.AccessibilityNodeInfo; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
||||
|
||||
import java.lang.ref.WeakReference; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; |
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHost; |
||||
|
||||
public class TextureRenderView extends TextureView implements IRenderView { |
||||
|
||||
private MeasureHelper mMeasureHelper; |
||||
|
||||
public TextureRenderView(Context context) { |
||||
super(context); |
||||
initView(context); |
||||
} |
||||
|
||||
public TextureRenderView(Context context, AttributeSet attrs) { |
||||
super(context, attrs); |
||||
initView(context); |
||||
} |
||||
|
||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||
super(context, attrs, defStyleAttr); |
||||
initView(context); |
||||
} |
||||
|
||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
||||
super(context, attrs, defStyleAttr, defStyleRes); |
||||
initView(context); |
||||
} |
||||
|
||||
private void initView(Context context) { |
||||
mMeasureHelper = new MeasureHelper(this); |
||||
mSurfaceCallback = new SurfaceCallback(this); |
||||
setSurfaceTextureListener(mSurfaceCallback); |
||||
} |
||||
|
||||
@Override |
||||
public View getView() { |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldWaitForResize() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
protected void onDetachedFromWindow() { |
||||
mSurfaceCallback.willDetachFromWindow(); |
||||
super.onDetachedFromWindow(); |
||||
mSurfaceCallback.didDetachFromWindow(); |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoSize(int videoWidth, int videoHeight) { |
||||
if (videoWidth > 0 && videoHeight > 0) { |
||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight); |
||||
requestLayout(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
||||
if (videoSarNum > 0 && videoSarDen > 0) { |
||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); |
||||
requestLayout(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setVideoRotation(int degree) { |
||||
mMeasureHelper.setVideoRotation(degree); |
||||
setRotation(degree); |
||||
} |
||||
|
||||
@Override |
||||
public void setAspectRatio(int aspectRatio) { |
||||
mMeasureHelper.setAspectRatio(aspectRatio); |
||||
requestLayout(); |
||||
} |
||||
|
||||
@Override |
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); |
||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); |
||||
} |
||||
|
||||
public IRenderView.ISurfaceHolder getSurfaceHolder() { |
||||
return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture); |
||||
} |
||||
|
||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { |
||||
|
||||
private final TextureRenderView mTextureView; |
||||
private final SurfaceTexture mSurfaceTexture; |
||||
|
||||
private InternalSurfaceHolder(@NonNull TextureRenderView textureView, @Nullable SurfaceTexture surfaceTexture) { |
||||
mTextureView = textureView; |
||||
mSurfaceTexture = surfaceTexture; |
||||
} |
||||
|
||||
public void bindToMediaPlayer(IMediaPlayer mp) { |
||||
if (mp == null) return; |
||||
if (mp instanceof ISurfaceTextureHolder) { |
||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; |
||||
mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false); |
||||
SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture(); |
||||
if (surfaceTexture != null) { |
||||
mTextureView.setSurfaceTexture(surfaceTexture); |
||||
} else { |
||||
textureHolder.setSurfaceTexture(mSurfaceTexture); |
||||
textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback); |
||||
} |
||||
} else { |
||||
mp.setSurface(openSurface()); |
||||
} |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public IRenderView getRenderView() { |
||||
return mTextureView; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public SurfaceHolder getSurfaceHolder() { |
||||
return null; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public SurfaceTexture getSurfaceTexture() { |
||||
return mSurfaceTexture; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Surface openSurface() { |
||||
if (mSurfaceTexture == null) return null; |
||||
return new Surface(mSurfaceTexture); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void addRenderCallback(@NonNull IRenderCallback callback) { |
||||
mSurfaceCallback.addRenderCallback(callback); |
||||
} |
||||
|
||||
@Override |
||||
public void removeRenderCallback(@NonNull IRenderCallback callback) { |
||||
mSurfaceCallback.removeRenderCallback(callback); |
||||
} |
||||
|
||||
private SurfaceCallback mSurfaceCallback; |
||||
|
||||
private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost { |
||||
|
||||
private SurfaceTexture mSurfaceTexture; |
||||
private boolean mIsFormatChanged; |
||||
private int mWidth; |
||||
private int mHeight; |
||||
|
||||
private boolean mOwnSurfaceTexture = true; |
||||
private boolean mWillDetachFromWindow = false; |
||||
private boolean mDidDetachFromWindow = false; |
||||
|
||||
private final WeakReference<TextureRenderView> mWeakRenderView; |
||||
private final Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<>(); |
||||
|
||||
private SurfaceCallback(@NonNull TextureRenderView renderView) { |
||||
mWeakRenderView = new WeakReference<>(renderView); |
||||
} |
||||
|
||||
private void setOwnSurfaceTexture(boolean ownSurfaceTexture) { |
||||
mOwnSurfaceTexture = ownSurfaceTexture; |
||||
} |
||||
|
||||
private void addRenderCallback(@NonNull IRenderCallback callback) { |
||||
mRenderCallbackMap.put(callback, callback); |
||||
ISurfaceHolder surfaceHolder = null; |
||||
if (mSurfaceTexture != null) { |
||||
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture); |
||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); |
||||
} |
||||
if (mIsFormatChanged) { |
||||
if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture); |
||||
callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight); |
||||
} |
||||
} |
||||
|
||||
private void removeRenderCallback(@NonNull IRenderCallback callback) { |
||||
mRenderCallbackMap.remove(callback); |
||||
} |
||||
|
||||
@Override |
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { |
||||
mSurfaceTexture = surface; |
||||
mIsFormatChanged = false; |
||||
mWidth = 0; |
||||
mHeight = 0; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); |
||||
} |
||||
|
||||
@Override |
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
||||
mSurfaceTexture = surface; |
||||
mIsFormatChanged = true; |
||||
mWidth = width; |
||||
mHeight = height; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height); |
||||
} |
||||
|
||||
@Override |
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
||||
mSurfaceTexture = surface; |
||||
mIsFormatChanged = false; |
||||
mWidth = 0; |
||||
mHeight = 0; |
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface); |
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) renderCallback.onSurfaceDestroyed(surfaceHolder); |
||||
return mOwnSurfaceTexture; |
||||
} |
||||
|
||||
@Override |
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
||||
} |
||||
|
||||
@Override |
||||
public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) { |
||||
if (surfaceTexture == null) return; |
||||
if (mDidDetachFromWindow) { |
||||
if (surfaceTexture != mSurfaceTexture) { |
||||
surfaceTexture.release(); |
||||
} else if (!mOwnSurfaceTexture) { |
||||
surfaceTexture.release(); |
||||
} |
||||
} else if (mWillDetachFromWindow) { |
||||
if (surfaceTexture != mSurfaceTexture) { |
||||
surfaceTexture.release(); |
||||
} else if (!mOwnSurfaceTexture) { |
||||
setOwnSurfaceTexture(true); |
||||
} |
||||
} else { |
||||
if (surfaceTexture != mSurfaceTexture) { |
||||
surfaceTexture.release(); |
||||
} else if (!mOwnSurfaceTexture) { |
||||
setOwnSurfaceTexture(true); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void willDetachFromWindow() { |
||||
mWillDetachFromWindow = true; |
||||
} |
||||
|
||||
private void didDetachFromWindow() { |
||||
mDidDetachFromWindow = true; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) { |
||||
super.onInitializeAccessibilityEvent(event); |
||||
event.setClassName(TextureRenderView.class.getName()); |
||||
} |
||||
|
||||
@Override |
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { |
||||
super.onInitializeAccessibilityNodeInfo(info); |
||||
info.setClassName(TextureRenderView.class.getName()); |
||||
} |
||||
} |
||||
@ -0,0 +1,128 @@ |
||||
package com.lodz.android.mmsplayer.ijk.setting; |
||||
|
||||
|
||||
import androidx.annotation.IntDef; |
||||
import com.lodz.android.mmsplayer.ijk.media.IRenderView; |
||||
|
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
public class IjkPlayerSetting { |
||||
|
||||
public static IjkPlayerSetting getDefault(){ |
||||
IjkPlayerSetting setting = new IjkPlayerSetting(); |
||||
setting.playerType = PlayerType.PLAY_IJK; |
||||
setting.isBackgroundPlay = false; |
||||
setting.isUsingMediaCodec = true; |
||||
setting.isUsingMediaCodecAutoRotate = true; |
||||
setting.isMediaCodecHandleResolutionChange = true; |
||||
setting.isUsingOpenSLES = true; |
||||
setting.pixelFormatType = PixelFormatType.PIXEL_AUTO; |
||||
setting.isUsingMediaDataSource = true; |
||||
setting.renderViewType = RenderViewType.TEXTURE_VIEW; |
||||
setting.isEnableDetachedSurfaceTexture = true; |
||||
setting.aspectRatioType = IRenderView.AR_ASPECT_FIT_PARENT; |
||||
return setting; |
||||
} |
||||
|
||||
//--------------------------- 播放器设置 -----------------------------------
|
||||
@Retention(RetentionPolicy.SOURCE) |
||||
@IntDef({PlayerType.PALY_ANDROID_MEDIA, PlayerType.PLAY_IJK}) |
||||
public @interface PlayerType { |
||||
/** android原生播放器 */ |
||||
int PALY_ANDROID_MEDIA = 1; |
||||
/** Ijk播放器 */ |
||||
int PLAY_IJK = 2; |
||||
} |
||||
|
||||
/** 播放器类型 */ |
||||
@PlayerType |
||||
public int playerType = PlayerType.PLAY_IJK; |
||||
|
||||
//--------------------------- 后台播放设置 -----------------------------------
|
||||
/** 是否后台播放(4.0+) */ |
||||
public boolean isBackgroundPlay = false; |
||||
|
||||
//--------------------------- 硬解码设置 -----------------------------------
|
||||
/** 是否使用硬解码 */ |
||||
public boolean isUsingMediaCodec = true; |
||||
/** 是否使用硬解码下自动旋转 */ |
||||
public boolean isUsingMediaCodecAutoRotate = true; |
||||
/** 是否使用硬解码下处理分辨率更改 */ |
||||
public boolean isMediaCodecHandleResolutionChange = true; |
||||
|
||||
//--------------------------- 使用OpenGLES -----------------------------------
|
||||
/** 是否使用OpenGLES */ |
||||
public boolean isUsingOpenSLES = true; |
||||
|
||||
//--------------------------- 设置像素格式 -----------------------------------
|
||||
@Retention(RetentionPolicy.SOURCE) |
||||
@IntDef({PixelFormatType.PIXEL_AUTO, PixelFormatType.PIXEL_RGB_565, PixelFormatType.PIXEL_RGB_888, PixelFormatType.PIXEL_RGBX_8888, |
||||
PixelFormatType.PIXEL_YV12, PixelFormatType.PIXEL_OPENGL_ES2}) |
||||
public @interface PixelFormatType { |
||||
/** 自动选择 */ |
||||
int PIXEL_AUTO = 0; |
||||
/** RGB 565 */ |
||||
int PIXEL_RGB_565 = 1; |
||||
/** RGB 888 */ |
||||
int PIXEL_RGB_888 = 2; |
||||
/** RGBX 8888 */ |
||||
int PIXEL_RGBX_8888 = 3; |
||||
/** YV12 */ |
||||
int PIXEL_YV12 = 4; |
||||
/** OpenGL ES2 */ |
||||
int PIXEL_OPENGL_ES2 = 5; |
||||
} |
||||
|
||||
/** 像素格式 */ |
||||
@PixelFormatType |
||||
public int pixelFormatType = PixelFormatType.PIXEL_AUTO; |
||||
|
||||
//--------------------------- 使用数据源 -----------------------------------
|
||||
/** 是否使用数据源(需要6.0+且播放本地文件) */ |
||||
public boolean isUsingMediaDataSource = true; |
||||
|
||||
//--------------------------- 渲染的view -----------------------------------
|
||||
@Retention(RetentionPolicy.SOURCE) |
||||
@IntDef({RenderViewType.NO_VIEW, RenderViewType.SURFACE_VIEW, RenderViewType.TEXTURE_VIEW}) |
||||
public @interface RenderViewType { |
||||
/** 不使用渲染的view */ |
||||
int NO_VIEW = 0; |
||||
/** 用surfaceview渲染 */ |
||||
int SURFACE_VIEW = 1; |
||||
/** 用textureview渲染 */ |
||||
int TEXTURE_VIEW = 2; |
||||
} |
||||
|
||||
/** 渲染的view的类型 */ |
||||
@RenderViewType |
||||
public int renderViewType = RenderViewType.TEXTURE_VIEW; |
||||
|
||||
/** 是否使用SurfaceTexture处理视频图像 */ |
||||
public boolean isEnableDetachedSurfaceTexture = true; |
||||
|
||||
//--------------------------- 播放器长宽比类型 -----------------------------------
|
||||
@Retention(RetentionPolicy.SOURCE) |
||||
@IntDef({IRenderView.AR_ASPECT_FIT_PARENT, IRenderView.AR_ASPECT_FILL_PARENT, IRenderView.AR_ASPECT_WRAP_CONTENT, |
||||
IRenderView.AR_16_9_FIT_PARENT, IRenderView.AR_4_3_FIT_PARENT}) |
||||
public @interface AspectRatioType {} |
||||
@AspectRatioType |
||||
public int aspectRatioType = IRenderView.AR_ASPECT_FIT_PARENT; |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "IjkPlayerSetting{" + |
||||
"\n playerType=" + playerType + |
||||
", \n isBackgroundPlay=" + isBackgroundPlay + |
||||
", \n isUsingMediaCodec=" + isUsingMediaCodec + |
||||
", \n isUsingMediaCodecAutoRotate=" + isUsingMediaCodecAutoRotate + |
||||
", \n isMediaCodecHandleResolutionChange=" + isMediaCodecHandleResolutionChange + |
||||
", \n isUsingOpenSLES=" + isUsingOpenSLES + |
||||
", \n pixelFormatType=" + pixelFormatType + |
||||
", \n isUsingMediaDataSource=" + isUsingMediaDataSource + |
||||
", \n renderViewType=" + renderViewType + |
||||
", \n isEnableDetachedSurfaceTexture=" + isEnableDetachedSurfaceTexture + |
||||
", \n aspectRatioType=" + aspectRatioType + |
||||
'}'; |
||||
} |
||||
} |
||||
@ -0,0 +1,71 @@ |
||||
package com.lodz.android.mmsplayer.ijk.utils; |
||||
|
||||
import android.content.Context; |
||||
import android.text.TextUtils; |
||||
|
||||
import com.lodz.android.mmsplayer.R; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import tv.danmaku.ijk.media.player.misc.ITrackInfo; |
||||
|
||||
/** |
||||
* 视频信息帮助类 |
||||
* Created by zhouL on 2016/12/1. |
||||
*/ |
||||
public class MediaInfoUtils { |
||||
|
||||
public static String buildResolution(int width, int height, int sarNum, int sarDen) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
sb.append(width); |
||||
sb.append(" x "); |
||||
sb.append(height); |
||||
if (sarNum > 1 || sarDen > 1) { |
||||
sb.append("["); |
||||
sb.append(sarNum); |
||||
sb.append(":"); |
||||
sb.append(sarDen); |
||||
sb.append("]"); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public static String buildTimeMilli(long duration) { |
||||
long total_seconds = duration / 1000; |
||||
long hours = total_seconds / 3600; |
||||
long minutes = (total_seconds % 3600) / 60; |
||||
long seconds = total_seconds % 60; |
||||
if (duration <= 0) { |
||||
return "00:00"; |
||||
} |
||||
if (hours >= 100) { |
||||
return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds); |
||||
} else if (hours > 0) { |
||||
return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds); |
||||
} else { |
||||
return String.format(Locale.US, "%02d:%02d", minutes, seconds); |
||||
} |
||||
} |
||||
|
||||
public static String buildTrackType(Context context, int type) { |
||||
switch (type) { |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO: |
||||
return context.getString(R.string.mmsplayer_tracktype_video); |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO: |
||||
return context.getString(R.string.mmsplayer_tracktype_audio); |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE: |
||||
return context.getString(R.string.mmsplayer_tracktype_subtitle); |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT: |
||||
return context.getString(R.string.mmsplayer_tracktype_timedtext); |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_METADATA: |
||||
return context.getString(R.string.mmsplayer_tracktype_metadata); |
||||
case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN: |
||||
default: |
||||
return context.getString(R.string.mmsplayer_tracktype_unknown); |
||||
} |
||||
} |
||||
|
||||
public static String buildLanguage(String language) { |
||||
return TextUtils.isEmpty(language)? "N/A" : language; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue