最近在研究google的ExoPlayer,根据项目需求,需要获得当前帧的显示时间,看源码发现解码在MediaCodecVideoRenderer这个类中执行解码,发现processOutputBuffer函数内有时间数据,根据测试知道presentationTimeUs是显示时间,然后就是想办法把时间给我们了
既然想改源码,那先下载源码,源码
新建个module,把core,dash,hls,smoothstreaming,ui文件夹内的代码和资源都复制到这个module内
新建个interface VideoTimeListener
public interface VideoTimeListener {
void onVideoTimeChanged(long time);
}
修改MediaCodecVideoRenderer,添加一个VideoTimeListener和构造函数
private VideoTimeListener timeListener;
public MediaCodecVideoRenderer(Context context, MediaCodecSelector mediaCodecSelector,
long allowedJoiningTimeMs,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener,VideoTimeListener timeListener, int maxDroppedFramesToNotify) {
super(C.TRACK_TYPE_VIDEO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
this.context = context.getApplicationContext();
frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context);
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
deviceNeedsAutoFrcWorkaround = deviceNeedsAutoFrcWorkaround();
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
outputStreamOffsetUs = C.TIME_UNSET;
joiningDeadlineMs = C.TIME_UNSET;
currentWidth = Format.NO_VALUE;
currentHeight = Format.NO_VALUE;
currentPixelWidthHeightRatio = Format.NO_VALUE;
pendingPixelWidthHeightRatio = Format.NO_VALUE;
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
this.timeListener = timeListener;
clearReportedVideoSize();
}
在renderOutputBuffer和renderOutputBufferV21内添加
protected void renderOutputBuffer(MediaCodec codec, int index, long presentationTimeUs) {
//...省略代码
if(timeListener != null){
timeListener.onVideoTimeChanged(presentationTimeUs/1000);
}
}
@TargetApi(21)
protected void renderOutputBufferV21(
MediaCodec codec, int index, long presentationTimeUs, long releaseTimeNs) {
//...省略代码
if(timeListener != null){
timeListener.onVideoTimeChanged(presentationTimeUs/1000);
}
}
修改RenderersFactory的createRenderers
Renderer[] createRenderers(Handler eventHandler,
VideoRendererEventListener videoRendererEventListener,
AudioRendererEventListener audioRendererEventListener,
VideoTimeListener videoTimeListener,TextOutput textRendererOutput,
MetadataOutput metadataRendererOutput);
修改DefaultRenderersFactory的createRenderers和buildVideoRenderers
@Override
public Renderer[] createRenderers(Handler eventHandler,
VideoRendererEventListener videoRendererEventListener,
AudioRendererEventListener audioRendererEventListener,
VideoTimeListener videoTimeListener,
TextOutput textRendererOutput, MetadataOutput metadataRendererOutput) {
ArrayList<Renderer> renderersList = new ArrayList<>();
buildVideoRenderers(context, drmSessionManager, allowedVideoJoiningTimeMs,
eventHandler, videoRendererEventListener,videoTimeListener, extensionRendererMode, renderersList);
//省略代码
return renderersList.toArray(new Renderer[renderersList.size()]);
}
protected void buildVideoRenderers(Context context,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
long allowedVideoJoiningTimeMs, Handler eventHandler,
VideoRendererEventListener eventListener,VideoTimeListener videoTimeListener, @ExtensionRendererMode int extensionRendererMode,
ArrayList<Renderer> out) {
out.add(new MediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT,
allowedVideoJoiningTimeMs, drmSessionManager, false, eventHandler, eventListener,videoTimeListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
//省略代码
}
修改SimpleExoPlayer的构造函数
private final CopyOnWriteArraySet<VideoTimeListener> videoTimeListeners;
protected SimpleExoPlayer(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
Clock clock) {
componentListener = new ComponentListener();
videoListeners = new CopyOnWriteArraySet<>();
textOutputs = new CopyOnWriteArraySet<>();
metadataOutputs = new CopyOnWriteArraySet<>();
videoDebugListeners = new CopyOnWriteArraySet<>();
audioDebugListeners = new CopyOnWriteArraySet<>();
videoTimeListeners = new CopyOnWriteArraySet<>();
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
Handler eventHandler = new Handler(eventLooper);
renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener,
componentListener, componentListener,componentListener);
//省略代码
}
在SimpleExoPlayer添加函数
public void addVideoTiemListener(VideoTimeListener listener){
videoTimeListeners.add(listener);
}
public void removeVideoTiemListener(VideoTimeListener listener){
videoTimeListeners.remove(listener);
}
修改ComponentListener
ComponentListener implements VideoRendererEventListener,
AudioRendererEventListener, TextOutput, MetadataOutput, SurfaceHolder.Callback,
TextureView.SurfaceTextureListener,VideoTimeListener
@Override
public void onVideoTimeChanged(long time) {
for (VideoTimeListener videoTimeListener : videoTimeListeners) {
videoTimeListener.onVideoTimeChanged(time);
}
}
好了,准备工作完成,在exoplayer初始化的时候
player.addVideoTiemListener(new VideoTimeListener() {
@Override
public void onVideoTimeChanged(long time) {
videoTime = time;
}
});
经过和ffmpeg解码出来的数据对比
exoplayer数据
ffmpeg
例子代码
密码:addl