美文网首页
从demo分析ijk源码五:视频解码

从demo分析ijk源码五:视频解码

作者: DON_1007 | 来源:发表于2021-11-15 16:35 被阅读0次

    一、播放器的初始化

    ijk播放器初始化的时候会先调用native_setup

    static void
    IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    {
        MPTRACE("%s\n", __func__);
        IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
        JNI_CHECK_GOTO(mp, env, "java/lang/OutOfMemoryError", "mpjni: native_setup: ijkmp_create() failed", LABEL_RETURN);
    
        jni_set_media_player(env, thiz, mp);
        ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));
        ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    }
    

    创建播放器实例 IjkMediaPlayer

    IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
    {
        IjkMediaPlayer *mp = ijkmp_create(msg_loop);
        if (!mp)
            goto fail;
    
        mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
        if (!mp->ffplayer->vout)
            goto fail;
    
        mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
        if (!mp->ffplayer->pipeline)
            goto fail;
    
        ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
    
        return mp;
    
    fail:
        ijkmp_dec_ref_p(&mp);
        return NULL;
    }
    
    IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
    {
        IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
        if (!mp)
            goto fail;
    
        mp->ffplayer = ffp_create();
        if (!mp->ffplayer)
            goto fail;
    
        mp->msg_loop = msg_loop;
    
        ijkmp_inc_ref(mp);
        pthread_mutex_init(&mp->mutex, NULL);
    
        return mp;
    
        fail:
        ijkmp_destroy_p(&mp);
        return NULL;
    }
    
    FFPlayer *ffp_create()
    {
        av_log(NULL, AV_LOG_INFO, "av_version_info: %s\n", av_version_info());
        av_log(NULL, AV_LOG_INFO, "ijk_version_info: %s\n", ijk_version_info());
    
        FFPlayer* ffp = (FFPlayer*) av_mallocz(sizeof(FFPlayer));
        if (!ffp)
            return NULL;
    
        msg_queue_init(&ffp->msg_queue);
        ffp->af_mutex = SDL_CreateMutex();
        ffp->vf_mutex = SDL_CreateMutex();
    
        ffp_reset_internal(ffp);
        ffp->av_class = &ffp_context_class;
        ffp->meta = ijkmeta_create();
    
        av_opt_set_defaults(ffp);
    
        return ffp;
    }
    

    在初始化IJKMediaPlayer的过程中,设置了一个回调查找解码器的回调

    ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
    

    在回调方法 mediacodec_select_callback 中通过 J4AC_IjkMediaPlayer__onSelectCodec__withCString__asCBuffer来找解码器

    static bool mediacodec_select_callback(void *opaque, ijkmp_mediacodecinfo_context *mcc)
    {
        ...
    
        found_codec_name = J4AC_IjkMediaPlayer__onSelectCodec__withCString__asCBuffer(env, weak_this, mcc->mime_type, mcc->profile, mcc->level, mcc->codec_name, sizeof(mcc->codec_name));
        ...
        return found_codec_name;
    }
    

    最终通过jni,回调到java层查找最合适的解码器

        class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;
        name     = "onSelectCodec";
        sign     = "(Ljava/lang/Object;Ljava/lang/String;II)Ljava/lang/String;";
        class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_onSelectCodec = J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);
        if (class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_onSelectCodec == NULL)
            goto fail;
    
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public String onMediaCodecSelect(IMediaPlayer mp, String mimeType, int profile, int level) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
                    return null;
    
                if (TextUtils.isEmpty(mimeType))
                    return null;
    
                Log.i(TAG, String.format(Locale.US, "onSelectCodec: mime=%s, profile=%d, level=%d", mimeType, profile, level));
                ArrayList<IjkMediaCodecInfo> candidateCodecList = new ArrayList<IjkMediaCodecInfo>();
                int numCodecs = MediaCodecList.getCodecCount();
                for (int i = 0; i < numCodecs; i++) {
                    MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
                    Log.d(TAG, String.format(Locale.US, "  found codec: %s", codecInfo.getName()));
                    if (codecInfo.isEncoder())
                        continue;
    
                    String[] types = codecInfo.getSupportedTypes();
                    if (types == null)
                        continue;
    
                    for(String type: types) {
                        if (TextUtils.isEmpty(type))
                            continue;
    
                        Log.d(TAG, String.format(Locale.US, "    mime: %s", type));
                        if (!type.equalsIgnoreCase(mimeType))
                            continue;
    
                        IjkMediaCodecInfo candidate = IjkMediaCodecInfo.setupCandidate(codecInfo, mimeType);
                        if (candidate == null)
                            continue;
    
                        candidateCodecList.add(candidate);
                        Log.i(TAG, String.format(Locale.US, "candidate codec: %s rank=%d", codecInfo.getName(), candidate.mRank));
                        candidate.dumpProfileLevels(mimeType);
                    }
                }
    
                if (candidateCodecList.isEmpty()) {
                    return null;
                }
    
                IjkMediaCodecInfo bestCodec = candidateCodecList.get(0);
    
                for (IjkMediaCodecInfo codec : candidateCodecList) {
                    if (codec.mRank > bestCodec.mRank) {
                        bestCodec = codec;
                    }
                }
    
                if (bestCodec.mRank < IjkMediaCodecInfo.RANK_LAST_CHANCE) {
                    Log.w(TAG, String.format(Locale.US, "unaccetable codec: %s", bestCodec.mCodecInfo.getName()));
                    return null;
                }
    
                Log.i(TAG, String.format(Locale.US, "selected codec: %s rank=%d", bestCodec.mCodecInfo.getName(), bestCodec.mRank));
                return bestCodec.mCodecInfo.getName();
            }
    

    此回调方法会在播放器加载到视频资源并解析成功后调用,回调方法的一个重要参数 mimeType 是在视频数据解析成功后获得。

    二、设置surface

    第二个比较重要的步骤是给播放器提供一个解码渲染的surface

    static void
    IjkMediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
    {
        MPTRACE("%s\n", __func__);
        IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
        JNI_CHECK_GOTO(mp, env, NULL, "mpjni: setVideoSurface: null mp", LABEL_RETURN);
    
        ijkmp_android_set_surface(env, mp, jsurface);
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
        return;
    }
    
    void ijkmp_android_set_surface_l(JNIEnv *env, IjkMediaPlayer *mp, jobject android_surface)
    {
        if (!mp || !mp->ffplayer || !mp->ffplayer->vout)
            return;
    
        SDL_VoutAndroid_SetAndroidSurface(env, mp->ffplayer->vout, android_surface);
        ffpipeline_set_surface(env, mp->ffplayer->pipeline, android_surface);
    }
    

    创建ANativeWindow

    void SDL_VoutAndroid_SetAndroidSurface(JNIEnv *env, SDL_Vout *vout, jobject android_surface)
    {
        ANativeWindow *native_window = NULL;
        if (android_surface) {
            native_window = ANativeWindow_fromSurface(env, android_surface);
            if (!native_window) {
                ALOGE("%s: ANativeWindow_fromSurface: failed\n", __func__);
                // do not return fail here;
            }
        }
    
        SDL_VoutAndroid_SetNativeWindow(vout, native_window);
        if (native_window)
            ANativeWindow_release(native_window);
    }
    

    三、起播

    static void
    IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
    {
        MPTRACE("%s\n", __func__);
        int retval = 0;
        IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
        JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: prepareAsync: null mp", LABEL_RETURN);
    
        retval = ijkmp_prepare_async(mp);
        IJK_CHECK_MPRET_GOTO(retval, env, LABEL_RETURN);
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    }
    
    int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name)
    {
        assert(ffp);
        assert(!ffp->is);
        assert(file_name);
    
        if (av_stristart(file_name, "rtmp", NULL) ||
            av_stristart(file_name, "rtsp", NULL)) {
            // There is total different meaning for 'timeout' option in rtmp
            av_log(ffp, AV_LOG_WARNING, "remove 'timeout' option for rtmp.\n");
            av_dict_set(&ffp->format_opts, "timeout", NULL, 0);
        }
    
        /* there is a length limit in avformat */
        if (strlen(file_name) + 1 > 1024) {
            av_log(ffp, AV_LOG_ERROR, "%s too long url\n", __func__);
            if (avio_find_protocol_name("ijklongurl:")) {
                av_dict_set(&ffp->format_opts, "ijklongurl-url", file_name, 0);
                file_name = "ijklongurl:";
            }
        }
    
        av_log(NULL, AV_LOG_INFO, "===== versions =====\n");
        ...
    
        av_opt_set_dict(ffp, &ffp->player_opts);
        if (!ffp->aout) {
            ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);
            if (!ffp->aout)
                return -1;
        }
        ...
        VideoState *is = stream_open(ffp, file_name, NULL);
        if (!is) {
            av_log(NULL, AV_LOG_WARNING, "ffp_prepare_async_l: stream_open failed OOM");
            return EIJK_OUT_OF_MEMORY;
        }
    
        ffp->is = is;
        ffp->input_filename = av_strdup(file_name);
        return 0;
    }
    
    static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
    {
        ...
        is->video_refresh_tid = SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");
        if (!is->video_refresh_tid) {
            av_freep(&ffp->is);
            return NULL;
        }
    
        is->initialized_decoder = 0;
        is->read_tid = SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");
        if (!is->read_tid) {
            av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
            goto fail;
        }
    
        if (ffp->async_init_decoder && !ffp->video_disable && ffp->video_mime_type && strlen(ffp->video_mime_type) > 0
                        && ffp->mediacodec_default_name && strlen(ffp->mediacodec_default_name) > 0) {
            if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2) {
                decoder_init(&is->viddec, NULL, &is->videoq, is->continue_read_thread);
                ffp->node_vdec = ffpipeline_init_video_decoder(ffp->pipeline, ffp);
            }
        }
        is->initialized_decoder = 1;
    
        return is;
        ...
    }
    

    在stream_open中创建了两个线程,video_refresh_thread用于解码渲染,read_thread用于加载数据。

    先看read_thread

    static int read_thread(void *arg)
    {
    
         ...
    
        int video_stream_count = 0;
        int h264_stream_count = 0;
        int first_h264_stream = -1;
        for (i = 0; i < ic->nb_streams; i++) {
            AVStream *st = ic->streams[i];
            enum AVMediaType type = st->codecpar->codec_type;
            st->discard = AVDISCARD_ALL;
            if (type >= 0 && ffp->wanted_stream_spec[type] && st_index[type] == -1)
                if (avformat_match_stream_specifier(ic, st, ffp->wanted_stream_spec[type]) > 0)
                    st_index[type] = i;
    
            // choose first h264
    
            if (type == AVMEDIA_TYPE_VIDEO) {
                enum AVCodecID codec_id = st->codecpar->codec_id;
                video_stream_count++;
                if (codec_id == AV_CODEC_ID_H264) {
                    h264_stream_count++;
                    if (first_h264_stream < 0)
                        first_h264_stream = i;
                }
            }
        }
        if (video_stream_count > 1 && st_index[AVMEDIA_TYPE_VIDEO] < 0) {
            st_index[AVMEDIA_TYPE_VIDEO] = first_h264_stream;
            av_log(NULL, AV_LOG_WARNING, "multiple video stream found, prefer first h264 stream: %d\n", first_h264_stream);
        }
        if (!ffp->video_disable)
            st_index[AVMEDIA_TYPE_VIDEO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                                    st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
        if (!ffp->audio_disable)
            st_index[AVMEDIA_TYPE_AUDIO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
                                    st_index[AVMEDIA_TYPE_AUDIO],
                                    st_index[AVMEDIA_TYPE_VIDEO],
                                    NULL, 0);
        if (!ffp->video_disable && !ffp->subtitle_disable)
            st_index[AVMEDIA_TYPE_SUBTITLE] =
                av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
                                    st_index[AVMEDIA_TYPE_SUBTITLE],
                                    (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
                                     st_index[AVMEDIA_TYPE_AUDIO] :
                                     st_index[AVMEDIA_TYPE_VIDEO]),
                                    NULL, 0);
        ...
    
        /* open the streams */
        if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
            stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);
        } else {
            ffp->av_sync_type = AV_SYNC_VIDEO_MASTER;
            is->av_sync_type  = ffp->av_sync_type;
        }
    
        ret = -1;
        if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
            ret = stream_component_open(ffp, st_index[AVMEDIA_TYPE_VIDEO]);
        }
        if (is->show_mode == SHOW_MODE_NONE)
            is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
    
        if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
            stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]);
        }
        ffp_notify_msg1(ffp, FFP_MSG_COMPONENT_OPEN);
    
        ...
    
        if (is->video_st && is->video_st->codecpar) {
            AVCodecParameters *codecpar = is->video_st->codecpar;
            ffp_notify_msg3(ffp, FFP_MSG_VIDEO_SIZE_CHANGED, codecpar->width, codecpar->height);
            ffp_notify_msg3(ffp, FFP_MSG_SAR_CHANGED, codecpar->sample_aspect_ratio.num, codecpar->sample_aspect_ratio.den);
        }
        ffp->prepared = true;
        ffp_notify_msg1(ffp, FFP_MSG_PREPARED);
    
        for (;;) {
         if ((!is->paused || completed) &&
                (!is->audio_st || (is->auddec.finished == is->audioq.serial && frame_queue_nb_remaining(&is->sampq) == 0)) &&
                (!is->video_st || (is->viddec.finished == is->videoq.serial && frame_queue_nb_remaining(&is->pictq) == 0))) {
                        ...
                        if (ffp->error) {
                            av_log(ffp, AV_LOG_INFO, "ffp_toggle_buffering: error: %d\n", ffp->error);
                            ffp_notify_msg1(ffp, FFP_MSG_ERROR);
                        } else {
                            av_log(ffp, AV_LOG_INFO, "ffp_toggle_buffering: completed: OK\n");
                            ffp_notify_msg1(ffp, FFP_MSG_COMPLETED);
                        }
                }
            }
    
            pkt->flags = 0;
            ret = av_read_frame(ic, pkt);
            ...
    
            /* check if packet is in play range specified by user, then queue, otherwise discard */
            stream_start_time = ic->streams[pkt->stream_index]->start_time;
            pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
            pkt_in_play_range = ffp->duration == AV_NOPTS_VALUE ||
                    (pkt_ts - (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
                    av_q2d(ic->streams[pkt->stream_index]->time_base) -
                    (double)(ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0) / 1000000
                    <= ((double)ffp->duration / 1000000);
            if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
                packet_queue_put(&is->audioq, pkt);
            } else if (pkt->stream_index == is->video_stream && pkt_in_play_range
                       && !(is->video_st && (is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC))) {
                packet_queue_put(&is->videoq, pkt);
            } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
                packet_queue_put(&is->subtitleq, pkt);
            } else {
                av_packet_unref(pkt);
            }
    
        }
        return 0;
    }
    

    从上述代码可以看出 read_thread就是从服务器下载数据并存入相应的音视频数据队列。
    另外还有一个很重要的功能,从视频头数据中找到并初始化所需的解码器,这一块的代码逻辑在stream_component_open 中

    static int stream_component_open(FFPlayer *ffp, int stream_index)
    {
        ...
        codec = avcodec_find_decoder(avctx->codec_id);
    
        switch (avctx->codec_type) {
            case AVMEDIA_TYPE_AUDIO   : is->last_audio_stream    = stream_index; forced_codec_name = ffp->audio_codec_name; break;
            case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = ffp->subtitle_codec_name; break;
            case AVMEDIA_TYPE_VIDEO   : is->last_video_stream    = stream_index; forced_codec_name = ffp->video_codec_name; break;
            default: break;
        }
        if (forced_codec_name)
            codec = avcodec_find_decoder_by_name(forced_codec_name);
        ...
        switch (avctx->codec_type) {
            ...
        case AVMEDIA_TYPE_VIDEO:
            ...
            if (ffp->async_init_decoder) {
                ...
                if (ffp->node_vdec) {
                    is->viddec.avctx = avctx;
                    ret = ffpipeline_config_video_decoder(ffp->pipeline, ffp);
                }
                if (ret || !ffp->node_vdec) {
                    decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
                    ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
                    if (!ffp->node_vdec)
                        goto fail;
                }
            } else {
                decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
                ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
                if (!ffp->node_vdec)
                    goto fail;
            }
            if ((ret = decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")) < 0)
                goto out;
            ...
            break;
            ...
        default:
            break;
        }
        ...
    }
    
    static int func_config_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
    {
        IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
        int                       ret = NULL;
    
        if (ffp->node_vdec) {
            ret = ffpipenode_config_from_android_mediacodec(ffp, pipeline, opaque->weak_vout, ffp->node_vdec);
        }
    
        return ret;
    }
    
    int ffpipenode_config_from_android_mediacodec(FFPlayer *ffp, IJKFF_Pipeline *pipeline, SDL_Vout *vout, IJKFF_Pipenode *node) {
        int                   ret     = 0;
        VideoState            *is     = ffp->is;
        IJKFF_Pipenode_Opaque *opaque = node->opaque;
        JNIEnv                *env    = NULL;
        jobject              jsurface = NULL;
        opaque->decoder               = &is->viddec;
    
        if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) {
            ALOGE("%s:create: SetupThreadEnv failed\n", __func__);
            goto fail;
        }
    
        ret = avcodec_parameters_from_context(opaque->codecpar, opaque->decoder->avctx);
        if (ret)
            goto fail;
    
        switch (opaque->codecpar->codec_id) {
        case AV_CODEC_ID_H264:
            if (!ffp->mediacodec_avc && !ffp->mediacodec_all_videos) {
                ALOGE("%s: MediaCodec: AVC/H264 is disabled. codec_id:%d \n", __func__, opaque->codecpar->codec_id);
                goto fail;
            }
            switch (opaque->codecpar->profile) {
                case FF_PROFILE_H264_BASELINE:
                    ALOGI("%s: MediaCodec: H264_BASELINE: enabled\n", __func__);
                    break;
                case FF_PROFILE_H264_CONSTRAINED_BASELINE:
                    ALOGI("%s: MediaCodec: H264_CONSTRAINED_BASELINE: enabled\n", __func__);
                    break;
                case FF_PROFILE_H264_MAIN:
                    ALOGI("%s: MediaCodec: H264_MAIN: enabled\n", __func__);
                    break;
                case FF_PROFILE_H264_EXTENDED:
                    ALOGI("%s: MediaCodec: H264_EXTENDED: enabled\n", __func__);
                    break;
                case FF_PROFILE_H264_HIGH:
                    ALOGI("%s: MediaCodec: H264_HIGH: enabled\n", __func__);
                    break;
                case FF_PROFILE_H264_HIGH_10:
                    ALOGW("%s: MediaCodec: H264_HIGH_10: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_10_INTRA:
                    ALOGW("%s: MediaCodec: H264_HIGH_10_INTRA: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_422:
                    ALOGW("%s: MediaCodec: H264_HIGH_10_422: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_422_INTRA:
                    ALOGW("%s: MediaCodec: H264_HIGH_10_INTRA: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_444:
                    ALOGW("%s: MediaCodec: H264_HIGH_10_444: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
                    ALOGW("%s: MediaCodec: H264_HIGH_444_PREDICTIVE: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_HIGH_444_INTRA:
                    ALOGW("%s: MediaCodec: H264_HIGH_444_INTRA: disabled\n", __func__);
                    goto fail;
                case FF_PROFILE_H264_CAVLC_444:
                    ALOGW("%s: MediaCodec: H264_CAVLC_444: disabled\n", __func__);
                    goto fail;
                default:
                    ALOGW("%s: MediaCodec: (%d) unknown profile: disabled\n", __func__, opaque->codecpar->profile);
                    goto fail;
            }
            strcpy(opaque->mcc.mime_type, SDL_AMIME_VIDEO_AVC);
            opaque->mcc.profile = opaque->codecpar->profile;
            opaque->mcc.level   = opaque->codecpar->level;
            break;
        case AV_CODEC_ID_HEVC:
            if (!ffp->mediacodec_hevc && !ffp->mediacodec_all_videos) {
                ALOGE("%s: MediaCodec/HEVC is disabled. codec_id:%d \n", __func__, opaque->codecpar->codec_id);
                goto fail;
            }
            strcpy(opaque->mcc.mime_type, SDL_AMIME_VIDEO_HEVC);
            opaque->mcc.profile = opaque->codecpar->profile;
            opaque->mcc.level   = opaque->codecpar->level;
            break;
        case AV_CODEC_ID_MPEG2VIDEO:
            if (!ffp->mediacodec_mpeg2 && !ffp->mediacodec_all_videos) {
                ALOGE("%s: MediaCodec/MPEG2VIDEO is disabled. codec_id:%d \n", __func__, opaque->codecpar->codec_id);
                goto fail;
            }
            strcpy(opaque->mcc.mime_type, SDL_AMIME_VIDEO_MPEG2VIDEO);
            opaque->mcc.profile = opaque->codecpar->profile;
            opaque->mcc.level   = opaque->codecpar->level;
            break;
        case AV_CODEC_ID_MPEG4:
            if (!ffp->mediacodec_mpeg4 && !ffp->mediacodec_all_videos) {
                ALOGE("%s: MediaCodec/MPEG4 is disabled. codec_id:%d \n", __func__, opaque->codecpar->codec_id);
                goto fail;
            }
            if ((opaque->codecpar->codec_tag & 0x0000FFFF) == 0x00005844) {
                ALOGE("%s: divx is not supported \n", __func__);
                goto fail;
            }
            strcpy(opaque->mcc.mime_type, SDL_AMIME_VIDEO_MPEG4);
            opaque->mcc.profile = opaque->codecpar->profile >= 0 ? opaque->codecpar->profile : 0;
            opaque->mcc.level   = opaque->codecpar->level >= 0 ? opaque->codecpar->level : 1;
            break;
    
        default:
            ALOGE("%s:create: not H264 or H265/HEVC, codec_id:%d \n", __func__, opaque->codecpar->codec_id);
            goto fail;
        }
    
        if (strcmp(opaque->mcc.mime_type, ffp->video_mime_type)) {
            ALOGW("amc: video_mime_type error opaque->mcc.mime_type = %s\n", opaque->mcc.mime_type);
            goto fail;
        }
    
        ret = recreate_format_l(env, node);
        if (ret) {
            ALOGE("amc: recreate_format_l failed\n");
            goto fail;
        }
    
        jsurface = ffpipeline_get_surface_as_global_ref(env, pipeline);
        ret = configure_codec_l(env, node, jsurface);
        J4A_DeleteGlobalRef__p(env, &jsurface);
        if (ret != 0)
            goto fail;
    
        ffp_set_video_codec_info(ffp, MEDIACODEC_MODULE_NAME, opaque->mcc.codec_name);
    
        opaque->off_buf_out = 0;
        if (opaque->n_buf_out) {
            int i;
    
            opaque->amc_buf_out = calloc(opaque->n_buf_out, sizeof(*opaque->amc_buf_out));
            assert(opaque->amc_buf_out != NULL);
            for (i = 0; i < opaque->n_buf_out; i++)
                opaque->amc_buf_out[i].pts = AV_NOPTS_VALUE;
        }
    
        SDL_SpeedSamplerReset(&opaque->sampler);
        ffp->stat.vdec_type = FFP_PROPV_DECODER_MEDIACODEC;
    
        return 0;
    
    fail:
        ret = -1;
        ffpipenode_free_p(&node);
        return ret;
    }
    

    到了这一块,已经加载到了视频数据,并且已经找到了合适的解码器

    四、解码

    创建好解码器之后,启动了 video_thread 来解码数据,
    video_refresh_thread 用于渲染

    static int video_refresh_thread(void *arg)
    {
        FFPlayer *ffp = arg;
        VideoState *is = ffp->is;
        double remaining_time = 0.0;
        while (!is->abort_request) {
            if (remaining_time > 0.0)
                av_usleep((int)(int64_t)(remaining_time * 1000000.0));
            remaining_time = REFRESH_RATE;
            if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
                video_refresh(ffp, &remaining_time);
        }
    
        return 0;
    }
    
    
    static void video_image_display2(FFPlayer *ffp)
    {
        Frame *vp;
        Frame *sp = NULL;
        vp = frame_queue_peek_last(&is->pictq);
    
        if (vp->bmp) {
            ...
            if (ffp->render_wait_start && !ffp->start_on_prepared && is->pause_req) {
                if (!ffp->first_video_frame_rendered) {
                    ffp->first_video_frame_rendered = 1;
                    ffp_notify_msg1(ffp, FFP_MSG_VIDEO_RENDERING_START);
                }
                while (is->pause_req && !is->abort_request) {
                    SDL_Delay(20);
                }
            }
            SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp);
            ffp->stat.vfps = SDL_SpeedSamplerAdd(&ffp->vfps_sampler, FFP_SHOW_VFPS_FFPLAY, "vfps[ffplay]");
            if (!ffp->first_video_frame_rendered) {
                ffp->first_video_frame_rendered = 1;
                ffp_notify_msg1(ffp, FFP_MSG_VIDEO_RENDERING_START);
            }
            ...
        }
    }
    
    int SDL_VoutDisplayYUVOverlay(SDL_Vout *vout, SDL_VoutOverlay *overlay)
    {
        if (vout && overlay && vout->display_overlay)
            return vout->display_overlay(vout, overlay);
    
        return -1;
    }
    

    相关文章

      网友评论

          本文标题:从demo分析ijk源码五:视频解码

          本文链接:https://www.haomeiwen.com/subject/rrfgqltx.html