日志分析音视频不同步问题

安卓日志中声音出来的关键字是什么?图像出来的关键字是什么?音视频不同步问题的日志分析关键字和思路。

你是要做一个视频播放器的项目吗

  • 你可以看下这个问题的回答https://ask.csdn.net/questions/7735931
  • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:程序员到底是吃青春饭还要奋斗?那么行业前景又如何?
  • 除此之外, 这篇博客: 一个解决滑动冲突新思路,做到视图之间无缝地嵌套滑动中的 首先,先把布局的基础工作做了,让界面显示出来。 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 我们新建一个类,命名为 NestedOverScrollLayout,继承自 ViewGroup,NestedScrollingParent3,有许多未实现的接口,我们先放着,空实现。

    open class NestedOverScrollLayout : ViewGroup, NestedScrollingParent3 {
    
        private var mVelocityTracker = VelocityTracker.obtain()
        private var mScroller = Scroller(context)
    
        private var mParentHelper: NestedScrollingParentHelper? = null
    
        private var mTouchSlop: Int = 0
        private var mMinimumVelocity: Float = 0f
        private var mMaximumVelocity: Float = 0f
        private var mCurrentVelocity: Float = 0f
    
        // 阻尼滑动参数
        private val mMaxDragRate = 2.5f
        private val mMaxDragHeight = 250
        private val mScreenHeightPixels = context.resources.displayMetrics.heightPixels
    
        private var mHandler: Handler? = null
        private var mNestedInProgress = false
        private var mIsAllowOverScroll = true           // 是否允许过渡滑动
        private var mPreConsumedNeeded = 0              // 在子 View 滑动前,此View需要滑动的距离
        private var mSpinner = 0f                       // 当前竖直方向上 translationY 的距离
    
        private var mReboundAnimator: ValueAnimator? = null
        private var mReboundInterpolator = ReboundInterpolator(INTERPOLATOR_VISCOUS_FLUID)
    
        private var mAnimationRunnable: Runnable? = null    // 用来实现fling时,先过度滑动再回弹的效果
        private var mVerticalPermit = false                 // 控制fling时等待contentView回到translation = 0 的位置
    
        private var mRefreshContent: View? = null
    
        constructor(context: Context) : super(context) {
            init()
        }
    
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
            init()
        }
    
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
            init()
        }
    
        private fun init() {
            setWillNotDraw(false)
            mHandler = Handler(Looper.getMainLooper())
            mParentHelper = NestedScrollingParentHelper(this)
            ViewConfiguration.get(context).let {
                mTouchSlop = it.scaledTouchSlop
                mMinimumVelocity = it.scaledMinimumFlingVelocity.toFloat()
                mMaximumVelocity = it.scaledMaximumFlingVelocity.toFloat()
            }
        }
    
        override fun onFinishInflate() {
            super.onFinishInflate()
            val childCount = super.getChildCount()
    
            for (i in 0 until childCount) {
                val childView = super.getChildAt(i)
                if (SmartUtil.isContentView(childView)) {
                    mRefreshContent = childView
                    break
                }
            }
        }
    
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            var minimumWidth = 0
            var minimumHeight = 0
            val thisView = this
            for (i in 0 until super.getChildCount()) {
                val childView = super.getChildAt(i)
                if (childView == null || childView.visibility == GONE) continue
    
                if (mRefreshContent == childView) {
                    mRefreshContent?.let { contentView ->
                        val lp = contentView.layoutParams
                        val mlp = lp as? MarginLayoutParams
                        val leftMargin = mlp?.leftMargin ?: 0
                        val rightMargin = mlp?.rightMargin ?: 0
                        val bottomMargin = mlp?.bottomMargin ?: 0
                        val topMargin = mlp?.topMargin ?: 0
                        val widthSpec = getChildMeasureSpec(
                            widthMeasureSpec,
                            thisView.paddingLeft + thisView.paddingRight + leftMargin + rightMargin, lp.width
                        )
                        val heightSpec = getChildMeasureSpec(
                            heightMeasureSpec,
                            thisView.paddingTop + thisView.paddingBottom + topMargin + bottomMargin, lp.height
                        )
                        contentView.measure(widthSpec, heightSpec)
                        minimumWidth += contentView.measuredWidth
                        minimumHeight += contentView.measuredHeight
                    }
                }
            }
    
            minimumWidth += thisView.paddingLeft + thisView.paddingRight
            minimumHeight += thisView.paddingTop + thisView.paddingBottom
            super.setMeasuredDimension(
                resolveSize(minimumWidth.coerceAtLeast(super.getSuggestedMinimumWidth()), widthMeasureSpec),
                resolveSize(minimumHeight.coerceAtLeast(super.getSuggestedMinimumHeight()), heightMeasureSpec)
            )
        }
    
        override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
            val thisView = this
            for (i in 0 until super.getChildCount()) {
                val childView = super.getChildAt(i)
                if (childView == null || childView.visibility == GONE) continue
    
                if (mRefreshContent == childView) {
                    mRefreshContent?.let { contentView ->
                        val lp = contentView.layoutParams
                        val mlp = lp as? MarginLayoutParams
                        val leftMargin = mlp?.leftMargin ?: 0
                        val topMargin = mlp?.topMargin ?: 0
    
                        val left = leftMargin + thisView.paddingLeft
                        val top = topMargin + thisView.paddingTop
                        val right = left + contentView.measuredWidth
                        val bottom = top + contentView.measuredHeight
    
                        contentView.layout(left, top, right, bottom)
                    }
                }
    
            }
        }
    
        override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
            return MarginLayoutParams(context, attrs)
        }
    
        override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
            return false
        }
    
        override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
        }
    
        override fun onStopNestedScroll(target: View, type: Int) {
        }
    
        override fun onNestedScroll(
            target: View,
            dxConsumed: Int,
            dyConsumed: Int,
            dxUnconsumed: Int,
            dyUnconsumed: Int,
            type: Int,
            consumed: IntArray
        ) {
        }
    
        override fun onNestedScroll(
            target: View,
            dxConsumed: Int,
            dyConsumed: Int,
            dxUnconsumed: Int,
            dyUnconsumed: Int,
            type: Int
        ) {
        }
    
        override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
        }
    }
    

    可以看到,类里面声明了很多属性,大部分现在还没有用到,不过现在加上,后面用到这些属性时,会再提到。

    init() 方法中,作必要的初始化工作:

    private fun init() {
        mHandler = Handler(Looper.getMainLooper())
        mParentHelper = NestedScrollingParentHelper(this)
        ViewConfiguration.get(context).let {
            mTouchSlop = it.scaledTouchSlop
            mMinimumVelocity = it.scaledMinimumFlingVelocity.toFloat()
            mMaximumVelocity = it.scaledMaximumFlingVelocity.toFloat()
        }
    }
    

    比如初始化一个 Handler,这个 handler 主要是为了后文做动画更新 UI 用的,所以传入主线程的 looper 即可。

    因为 NestedOverScrollLayout 需要支持嵌套滑动,并在嵌套滑动中扮演 Parent 的角色,所以还需要初始化一个 NestedScrollingParentHelper() 辅助完成嵌套滑动操作。

    最后是初始化一些变量:mTouchSlop,mMinimumVelocity,mMaximumVelocity,得到最小滑动距离阈值和滑动速度阈值。

    在布局加载结束时,找到可以滚动的 View 作为内容布局,并赋值给 mRefreshContent 属性。

    override fun onFinishInflate() {
        super.onFinishInflate()
        val childCount = super.getChildCount()
    
        for (i in 0 until childCount) {
            val childView = super.getChildAt(i)
            if (SmartUtil.isContentView(childView)) {
                mRefreshContent = childView
                break
            }
        }
    }
    
    // SmartUtil
    object SmartUtil {
    
        fun isScrollableView(view: View?): Boolean {
            return view is AbsListView
                    || view is ScrollView
                    || view is ScrollingView
                    || view is WebView
                    || view is NestedScrollingChild
        }
    
        fun isContentView(view: View?): Boolean {
            return isScrollableView(view)
                    || view is ViewPager
                    || view is NestedScrollingParent
        }
    
    }
    

    onMeasure() 和 onLayout() 就根据 mRefreshContent 进行测量和布局。

    为了简单起见,NestedOverScrollLayout 只会包含一个 RecyclerView,所以把这个 RecyclerView 的大小、位置测量了就行了。大家看代码估计也能懂,测量的细节就略过了。

    有了这些方法后呢,在布局中使用 NestedOverScrollLayout 就应该有内容显示出来了。

    我们新建一个 Activity,修改布局文件,给 RecyclerView 一些假数据:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/eventViewGroup"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        <com.jamgu.home.viewevent.nested.NestedOverScrollLayout
                android:id="@+id/eventView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#f1227f">
    
            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/vRecycler1"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center" />
    
        </com.jamgu.home.viewevent.nested.NestedOverScrollLayout>
    
    </FrameLayout>
    

    看看效果。

    3_布局初始化.gif

    接下来,我们希望在 RecyclerView 在内容滑动到边界时,将无法消耗的滑动距离,交给 NestedOverScrollLayout 处理,根据上文对 NSP 接口调用时机的分析,RecyclerView 处理完自身滑动后,剩下的距离会传入 NestedOverScrollLayout 的 onNestedScroll() 方法,因此接下来要实现这个方法。

  • 您还可以看一下 千锋老师的什么是云计算? 课程中的 杨哥知识普及小课堂之 什么是云计算?小节, 巩固相关知识点