Android IjkPlayer内核编译记(二)RTMP追帧优化

封面

转载请注明出处:https://www.jianshu.com/p/b287fa51bd50

本文出自 容华谢后的博客

0.写在前面

使用IjkPlayer内核播放RTMP视频流的时候,在网络不好的情况下,延迟会越来越高,因为项目对视频实时性有一定的要求,最近一直在解决这个问题,看了下源码中的Issues,发现很多人也遇到了这个问题,一通参数设置下去效果有点改善但不多,也试过编译别人已经改好的版本,发现效果也不太理想,无它,只能自己动手改改了。

先说下为什么会发生延迟的现象,本文只讨论拉流端的优化,当然实际项目应用的时候,推流端也要一起优化下。当网络波动的时候,拉流端读取不到数据,等网络恢复正常,会把之前的数据读取回来,这时缓存就会变大,但拉流端又无法一下子消耗这么多缓存,就会一直有延迟。

网上有两种解决方案,一种是当检测到缓存队列中视频时长大于设定的区间时,进行视频倍速播放,快速消耗缓存队列中的视频帧,优点是可以避免卡顿,实际试了下,缺点也很明显,就是视频追帧一直追不上。另一种也是检测缓存队列,当达到上限时,进行视频丢帧处理,试了下发现效果还可以,在此记录下,分享给各位同学。

1.GOP说明

GOP 全称是 Group of Pictures,顾名思义就是连续的一组图像,视频流可以理解成是由一张张的图片构成的,但是这样会造成视频的体积过大,那怎么样来减少体积呢,人们想到,连续的图片中会有很多重复的场景,每次都传输也太没有效率了,可不可以改成,传一张图片,然后后面只传这张图片变化的部分呢,于是便有了GOP的概念。

GOP是由 I-帧、P-帧、B-帧 组成的一个连续帧序列,一个视频又由多个GOP组成。

  • I-帧:就是我们常说的关键帧,是一张完整的图片,一个GOP中只有一个I-帧。

  • P-帧:预测编码图像帧,它需要之前的I-帧或P-帧作为参考帧进行编码。P-帧会分析出参考帧中总是不变的部分,只有变化的部分会被编码,一个GOP中有多个P-帧。

  • B-帧:双向预测编码图像帧,它需要同时使用之前的I-帧或P-帧和之后的I-帧或P-帧进行编码,记录的是前后参考帧之间的差别,一个GOP中有多个B-帧。

GOP

2.DTS & PTS

上面讲到,由于B-帧是双向预测的,需要未来的参考帧才能解码,所以就需要一些辅助解码的工具。

  • DTS:Decode Timestamp,解码时间戳。

  • PTS:Presentation Timestamp,显示时间戳。

编码器在编码时需要提供DTS和PTS,而解码器在解码时会根据DTS顺序解码,再根据PTS顺序播放。

PTS&DTS

可以看到,PTS顺序是图中的1-9顺序,对应的DTS顺序如下,在解码B-帧之前,要先解码P-参考帧。

3.源码修改

源码位置:/ijkmedia/ijkplayer/ff_ffplay.c

3.1 修改read_thread方法

在外部设置一个可配置的缓冲区参数max_cached_duration,用于设置缓冲区内视频长度的上限,当设置这个参数之后,即开启追帧功能。

在read_thread方法方法中,有一个for循环用于处理视频帧数据,在这里进行缓存区检测功能,我加了一个cache_check_period配置,如果不想检测太快的话,可以配置和这个参数,单位都是毫秒。

static int read_thread(void *arg)
{
    ...

    // 如果设置了缓冲区时间,realtime参数设置为0
    if (is->max_cached_duration > 0) {
        is->realtime = 0;
    }else{
        is->realtime = is_realtime(ic);
    }

    ...

    // 上一次的检测时间
    int64_t last_control_queue_time = 0;

    for (;;) {
        ...

        // 获取当前时间
        struct timeval time_now;
        gettimeofday(&time_now, NULL);
        int64_t current_time = (int64_t)time_now.tv_sec * 1000 + time_now.tv_usec / 1000;
        // 计算时间差
        int64_t time_diff = current_time - last_control_queue_time;
        av_log(ffp, AV_LOG_INFO, "control_queue_duration time_diff = %" PRId64, time_diff);

        // 缓存区检测
        if (is->max_cached_duration > 0 && time_diff > is->cache_check_period) {
            control_queue_duration(ffp, is);
            last_control_queue_time = current_time;
        }

        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);
        }
    }
}

3.2 control_queue_duration方法定义

当缓存时长大于设定的上限时,就会执行丢帧操作,其中视频和音频的丢帧操作类似,实际测试的时候,如果频繁丢帧,画面会很卡顿,所以加了一个丢帧的间隔配置cache_delete_period,用于限制丢帧的最小间隔。当缓存区满了,但是还没有到丢帧间隔,也不会进行丢帧,防止网慢的时候一直在丢帧。

// 上一次丢帧的时间
int64_t last_drop_time = 0;

static void control_queue_duration(FFPlayer *ffp, VideoState *is) {
    if (is->max_cached_duration <= 0) {
        return;
    }
    // 音频流
    if (is->audio_st) {
        return control_audio_queue_duration(ffp, is);
    }
    // 视频流
    if (is->video_st) {
        return control_video_queue_duration(ffp, is);
    }
}

static void control_video_queue_duration(FFPlayer *ffp, VideoState *is) {
    // 判断时间基是否有效
    int time_base_valid = is->video_st->time_base.den > 0 && is->video_st->time_base.num > 0;
    if (!time_base_valid) {
        return;
    }

    // 加锁
    SDL_LockMutex(is->videoq.mutex);

    // 包数量
    int nb_packets = is->videoq.nb_packets;
    // 缓存时长
    int64_t cached_duration = -1;
    // 总时长
    int64_t duration = 0;

    if (is->videoq.first_pkt && is->videoq.last_pkt) {
        // 计算队列中的总时长
        duration = is->videoq.last_pkt->pkt.pts - is->videoq.first_pkt->pkt.pts;
        // 计算缓存时长
        cached_duration = duration * av_q2d(is->video_st->time_base) * 1000;
    }

    // 解锁
    SDL_UnlockMutex(is->videoq.mutex);

    // 获取当前时间,毫秒
    struct timeval time_now;
    gettimeofday(&time_now, NULL);
    int64_t current_time = (int64_t)time_now.tv_sec * 1000 + time_now.tv_usec / 1000;
    // 计算时间差
    int64_t time_diff = current_time - last_drop_time;   

    av_log(NULL, AV_LOG_INFO, "video cached_duration = %lld, nb_packets = %d, time_diff = %" PRId64, 
        cached_duration, nb_packets, time_diff);

    // 如果大于设定的缓存时长 && 在设置的丢帧时间间隔内
    if (cached_duration > is->max_cached_duration && time_diff > is->cache_delete_period) {
        // 丢弃队列中的一半视频帧数据
        int64_t drop_to_pts = is->videoq.last_pkt->pkt.pts - (duration / 2);
        drop_queue_until_pts(&is->videoq, drop_to_pts);
        last_drop_time = current_time;
    }
}

static void control_audio_queue_duration(FFPlayer *ffp, VideoState *is) {
    int time_base_valid = is->audio_st->time_base.den > 0 && is->audio_st->time_base.num > 0;
    if (!time_base_valid) {
        return;
    }

    // 加锁
    SDL_LockMutex(is->audioq.mutex);

    // 包数量
    int nb_packets = is->audioq.nb_packets;
    // 缓存时长
    int64_t cached_duration = -1;
    // 总时长
    int64_t duration = 0;
    
    if (is->audioq.first_pkt && is->audioq.last_pkt) {
        // 计算队列中的总时长
        duration = is->audioq.last_pkt->pkt.pts - is->audioq.first_pkt->pkt.pts;
        // 计算缓存时长
        cached_duration = duration * av_q2d(is->audio_st->time_base) * 1000;
    }

    // 解锁
    SDL_UnlockMutex(is->audioq.mutex);

    // 获取当前时间
    struct timeval time_now;
    gettimeofday(&time_now, NULL);
    int64_t current_time = (int64_t)time_now.tv_sec * 1000 + time_now.tv_usec / 1000;
    // 计算时间差
    int64_t time_diff = current_time - last_drop_time;

    av_log(NULL, AV_LOG_INFO, "audio cached_duration = %lld, nb_packets = %d, time_diff = %" PRId64,
        cached_duration, nb_packets, time_diff);

    // 如果大于设定的缓存时长 && 在设置的丢帧时间间隔内
    if (cached_duration > is->max_cached_duration && time_diff > is->cache_delete_period) {
        // 丢弃队列中的一半音频帧数据
        int64_t drop_to_pts = is->audioq.last_pkt->pkt.pts - (duration / 2);
        drop_queue_until_pts(&is->audioq, drop_to_pts);
        last_drop_time = current_time;
    }
}

3.3 drop_queue_until_pts方法定义

如果缓存丢帧满足条件,则会调用下面的方法进行丢帧操作,每次丢帧都是丢完整的GOP组,否则会造成花屏。

static void drop_queue_until_pts(PacketQueue *q, int64_t drop_to_pts) {
    MyAVPacketList *pkt1 = NULL;
    // 记录删除的包数量
    int del_nb_packets = 0;
    while (pkt1 = q->first_pkt) {
        // 如果是关键帧且时间戳大于等于目标时间戳,跳出循环
        if ((pkt1->pkt.flags & AV_PKT_FLAG_KEY) && pkt1->pkt.pts >= drop_to_pts) {
            break;
        }
        q->first_pkt = pkt1->next;
        if (!q->first_pkt)
            q->last_pkt = NULL;
        q->nb_packets--;
        ++del_nb_packets;
        // 更新队列的大小
        q->size -= pkt1->pkt.size + sizeof(*pkt1);
        // 更新队列的总时长
        if (pkt1->pkt.duration > 0)
            q->duration -= pkt1->pkt.duration;
        av_free_packet(&pkt1->pkt);
#ifdef FFP_MERGE
        av_free(pkt1);
#else
        pkt1->next = q->recycle_pkt;
        q->recycle_pkt = pkt1;
#endif
    }
    av_log(NULL, AV_LOG_INFO, "del_nb_packets = %d", del_nb_packets);
}

3.4 外部配置

源码位置:/ijkmedia/ijkplayer/ff_ffplay_def.h

在VideoState中定义三个参数,分别是最大缓存时长,缓存检测间隔,缓存删除间隔。


typedef struct VideoState {
    ...

    int max_cached_duration;
    int cache_check_period;
    int cache_delete_period;
} VideoState;

在read_thread方法中获取下获取下:

// 获取最大缓存时长配置
AVDictionaryEntry *max_cached_duration_entry = av_dict_get(ffp->player_opts, "max_cached_duration", NULL, 0);
if (max_cached_duration_entry) {
    int max_cached_duration = atoi(max_cached_duration_entry->value);
    if (max_cached_duration <= 0) {
        is->max_cached_duration = 0;
    } else {
        is->max_cached_duration = max_cached_duration;
    }
} else {
    is->max_cached_duration = 0;
}

// 获取缓存检测间隔配置
AVDictionaryEntry *cache_check_period_entry = av_dict_get(ffp->player_opts, "cache_check_period", NULL, 0);
if (cache_check_period_entry) {
    int cache_check_period = atoi(cache_check_period_entry->value);
    if (cache_check_period <= 0) {
        is->cache_check_period = 0;
    } else {
        is->cache_check_period = cache_check_period;
    }
} else {
    is->cache_check_period = 0;
}

// 获取缓存删除间隔配置
AVDictionaryEntry *cache_delete_period_entry = av_dict_get(ffp->player_opts, "cache_delete_period", NULL, 0);
if (cache_delete_period_entry) {
    int cache_delete_period = atoi(cache_delete_period_entry->value);
    if (cache_delete_period <= 0) {
        is->cache_delete_period = 0;
    } else {
        is->cache_delete_period = cache_delete_period;
    }
} else {
    is->cache_delete_period = 0;
}
av_log(NULL, AV_LOG_INFO, "config: max_cached_duration = %d, cache_check_period = %d, cache_delete_period = %d", 
    is->max_cached_duration, is->cache_check_period, is->cache_delete_period);

4.参数配置

4.1 IjkPlayer参数

// 是否启用无限缓冲区
// 1.启用无限缓冲区,防止播放过程中出现缓冲中断 0.禁用无限缓冲区
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "infbuf", 1);

// 设置播放前的探测时间,指定探测多长时间的视频数据以确定流信息(单位:微秒)
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 50000);

// 设置播放前的最大探测时间,指定最大探测时间,以防止探测时间过长(单位:微秒)
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 50000);

// 每处理一个packet之后刷新io上下文
// 1.每处理一个数据包后刷新I/O上下文,以确保数据的连续性
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);

// 设置播放器的探测大小,指定探测数据包的大小,以确定流信息(单位:字节)
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 2048);

// 需要准备好后自动播放
// 1.播放器准备好后立即播放 0.播放器准备好后不会自动开始播放,需要手动启动
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);

// 启用或禁用硬件解码
// 1.启用硬件解码;0.使用软件解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);

// 关闭播放器缓冲,减少延迟
// 0.禁用数据包缓冲,可以降低播放延迟
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);

// 设置重连次数
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5);

// 设置缓存最长时间
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 3000);

// 设置缓存检测间隔配置
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "cache_check_period", 0);

// 设置缓存删除间隔配置
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "cache_delete_period", 10000);

// 设置是否开启环路过滤
// 0.开启,画面质量高,解码开销大
// 48.关闭,画面质量差点,解码开销小
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);

4.2 编译参数

#! /usr/bin/env bash

#--------------------
# Standard options:
export COMMON_FF_CFG_FLAGS=

# Licensing options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-gpl"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-nonfree"

# Configuration options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-runtime-cpudetect"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-gray"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-swscale-alpha"

# Program options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-programs"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffmpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffplay"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffprobe"

# Documentation options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-doc"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-htmlpages"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-manpages"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-podpages"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-txtpages"

# Component options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avdevice"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avcodec"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avformat"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avutil"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-network"

# Hardware accelerators:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-d3d11va"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-dxva2"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vaapi"
#export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vda"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vdpau"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-videotoolbox"

# Individual component options:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-encoders"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-encoder=png"

# ./configure --list-decoders
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-decoders"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=aac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=aac_latm"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=flv"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h264"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mp3*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp6f"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=flac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=hevc"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp8"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp9"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263i"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263p"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mjpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=ijk*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-hwaccels"

# ./configure --list-muxers
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-muxers"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mp4"

# ./configure --list-demuxers
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-demuxers"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=aac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=concat"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=data"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=flv"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=hls"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=live_flv"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mov"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mp3"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegps"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegts"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegvideo"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=flac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=hevc"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=webm_dash_manifest"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=sdp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mjpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=avi"

# ./configure --list-parsers
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-parsers"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=aac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=aac_latm"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=h264"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=flac"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=hevc"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpeg4"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpeg4video"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpegvideo"

# ./configure --list-bsf
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-bsfs"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=chomp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dca_core"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dump_extradata"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=hevc_mp4toannexb"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=imx_dump_header"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpeg2jpeg"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpega_dump_header"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mov2textsub"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mp3_header_decompress"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mpeg4_unpack_bframes"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=noise"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=remove_extradata"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=text2movsub"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=vp9_superframe"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=eac3_core"

# ./configure --list-protocols
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocols"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=async"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=bluray"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=concat"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=crypto"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmpcrypt"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=ffrtmphttp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=gopher"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=icecast"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=librtmp*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=libssh"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=md5"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmsh"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmst"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtmp*"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmpt"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=sctp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=srtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=subfile"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=unix"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-devices"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-filters"

# External library support:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-iconv"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-audiotoolbox"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-videotoolbox"

# Developer options (useful when working on FFmpeg itself):
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-linux-perf"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bzlib"

5.写在最后

GitHub传送门:https://github.com/alidili/ijkplayer

到这里,IjkPlayer源码的RTMP追帧优化就完成了,有问题可以给我留言评论,谢谢。

感谢:https://www.jianshu.com/p/d6a5d8756eec

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351