ijkplayer 优化

1. 添加teardown

1、解决方法就是在调用ijkmp_stop之前通过avformat_preclose_input 调用这个方法:

ijkplayer.c

//20241025
int ffp_pre_stop_l(FFPlayer *ffp)
{
    assert(ffp);
    VideoState *is = ffp->is;
    if (is){
        avformat_pre_close_input(&is->ic);
    }
    return 0;
}

2. ijkplayer.c 的ijkmp_stop_l 方法里的ffp_stop_l 方法前添加如下方法, 以此为准

ffp_pre_stop_l(mp->ffplayer);

3. ijkplayer/extra/ffmpeg/libavformat/rtspdec.c修改(所有涉及的rtspdec.c文件都需要有改动):

57行
//20241025
static int rtsp_read_pre_close(AVFormatContext *s)
{
    RTSPState *rt = s->priv_data;
    if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN))
        ff_rtsp_send_cmd_async(s,"TEARDOWN", rt->control_uri, NULL);
    return 0;
}

最后.read_close = rtsp_read_close之前 增加:

.read_pre_close = rtsp_read_pre_close,

4. 修改 ijkplayer/extra/ffmpeg/libavformat/avformat.h

(1)增加方法read_pre_close 接口

723
    /**20241025
    * The stream is going to be closed. The AVFormatContext and AVStreams are not
    * freed by this function
    */
    int (*read_pre_close)(struct AVFormatContext *);

(2)增加方法avformat_pre_close_input接口

2499

/** 20241025
 * Call before closing input AVFormatContext.
 */
void avformat_pre_close_input(AVFormatContext **s);

5、修改 ijkplayer/extra/ffmpeg/libavformat/utils.c ,实现pre_close_input

4205
//20241025
void avformat_pre_close_input(AVFormatContext **ps)
{
    AVFormatContext *s;
    if (!ps || !*ps)
        return;
    s = *ps;
    if (s->iformat)
        if (s->iformat->read_pre_close)
            s->iformat->read_pre_close(s);
}

2. 卡顿修改示列

原因分析:
ffmpeg 解析服务器rtsp 返回数据时,通过”$“和 ”return_on_interleaved_data“字段 来区分rtsp控制信息和音视
频数据包的开始字段。而读取数据是按照一个一个字符来进行的,当网络、服务器数据等原因造成数据包不完整
时,”ff_rtsp_read_reply“方法就会出现卡顿的现象
解决方案:
修改extra/ffmpeg/libavformat/rtsp.c

int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply,
                       unsigned char **content_ptr,
                       int return_on_interleaved_data, const char *method)
{
    RTSPState *rt = s->priv_data;
    char buf[4096], buf1[1024], *q;
    unsigned char ch;
    const char *p;
    int ret, content_length, line_count = 0, request = 0;
    unsigned char *content = NULL;

start:
    line_count = 0;
    request = 0;
    content = NULL;
    memset(reply, 0, sizeof(*reply));

    /* parse reply (XXX: use buffers) */
    rt->last_reply[0] = '\0';
    for (;;) {
        q = buf;
        for (;;) {
            ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1);
            av_log(s, AV_LOG_TRACE, "ret=%d c=%02x [%c]\n", ret, ch, ch);
            if (ret != 1)
                return AVERROR_EOF;
            if (ch == '\n')
                break;
            //if (ch == '$' && q == buf) { magical note
            if (ch == '$') { //magical add
                if (return_on_interleaved_data) {
                    return 1;
                } else
                    ff_rtsp_skip_packet(s);
            } else if (ch != '\r') {
                if ((q - buf) < sizeof(buf) - 1)
                    *q++ = ch;
            }
        }
        *q = '\0';

        av_log(s, AV_LOG_TRACE, "line='%s'\n", buf);

        /* test if last line */
        if (buf[0] == '\0')
            break;
        p = buf;
        if (line_count == 0) {
            /* get reply code */
            get_word(buf1, sizeof(buf1), &p);
            if (!strncmp(buf1, "RTSP/", 5)) {
                get_word(buf1, sizeof(buf1), &p);
                reply->status_code = atoi(buf1);
                av_strlcpy(reply->reason, p, sizeof(reply->reason));
            } else {
                av_strlcpy(reply->reason, buf1, sizeof(reply->reason)); // method
                get_word(buf1, sizeof(buf1), &p); // object
                request = 1;
            }
        } else {
            //--------------------magical-s---------------
            if (return_on_interleaved_data) {
                av_log(s, AV_LOG_WARNING, "ADD ADD ------------ RETURN return_on_interleaved_data='%d',line_count = %d --------------ADD ADD.\n", return_on_interleaved_data, line_count);
                return 0;
            }
            //----------------------e--------------
            ff_rtsp_parse_line(s, reply, p, rt, method);
            av_strlcat(rt->last_reply, p,    sizeof(rt->last_reply));
            av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply));
        }
        line_count++;
    }

    if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0' && !request)
        av_strlcpy(rt->session_id, reply->session_id, sizeof(rt->session_id));

    content_length = reply->content_length;
    if (content_length > 0) {
        /* leave some room for a trailing '\0' (useful for simple parsing) */
        content = av_malloc(content_length + 1);
        if (!content)
            return AVERROR(ENOMEM);
        ffurl_read_complete(rt->rtsp_hd, content, content_length);
        content[content_length] = '\0';
    }
    if (content_ptr)
        *content_ptr = content;
    else
        av_freep(&content);

    if (request) {
        //----------magical -----s--------------
        //add 20240220 If normal data packets arrive here, it indicates that abnormal data is parsed, and it directly returns 0, not request the server
        if (return_on_interleaved_data) {
            av_log(s, AV_LOG_WARNING, "ADD ADD ------------ request RETURN return_on_interleaved_data='%d' --------------ADD ADD.\n", return_on_interleaved_data);
            return 0;
        }
        //-----------------------------
        char buf[1024];
        char base64buf[AV_BASE64_SIZE(sizeof(buf))];
        const char* ptr = buf;

        if (!strcmp(reply->reason, "OPTIONS")) {
            snprintf(buf, sizeof(buf), "RTSP/1.0 200 OK\r\n");
            if (reply->seq)
                av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", reply->seq);
            if (reply->session_id[0])
                av_strlcatf(buf, sizeof(buf), "Session: %s\r\n",
                                              reply->session_id);
        } else {
            snprintf(buf, sizeof(buf), "RTSP/1.0 501 Not Implemented\r\n");
        }
        av_strlcat(buf, "\r\n", sizeof(buf));

        if (rt->control_transport == RTSP_MODE_TUNNEL) {
            av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf));
            ptr = base64buf;
        }
        ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr));

        rt->last_cmd_time = av_gettime_relative();
        /* Even if the request from the server had data, it is not the data
         * that the caller wants or expects. The memory could also be leaked
         * if the actual following reply has content data. */
        if (content_ptr)
            av_freep(content_ptr);
        /* If method is set, this is called from ff_rtsp_send_cmd,
         * where a reply to exactly this request is awaited. For
         * callers from within packet receiving, we just want to
         * return to the caller and go back to receiving packets. */
        if (method)
            goto start;
        return 0;
    }

    if (rt->seq != reply->seq) {
        av_log(s, AV_LOG_WARNING, "CSeq %d expected, %d received.\n",
            rt->seq, reply->seq);
    }

    /* EOS */
    if (reply->notice == 2101 /* End-of-Stream Reached */      ||
        reply->notice == 2104 /* Start-of-Stream Reached */    ||
        reply->notice == 2306 /* Continuous Feed Terminated */) {
        rt->state = RTSP_STATE_IDLE;
    } else if (reply->notice >= 4400 && reply->notice < 5500) {
        return AVERROR(EIO); /* data or server error */
    } else if (reply->notice == 2401 /* Ticket Expired */ ||
             (reply->notice >= 5500 && reply->notice < 5600) /* end of term */ )
        return AVERROR(EPERM);

    return 0;
}

3. 花屏解决实例

1. h264_parse.c

add

#include "libavformat/avformat.h"
gFrameErorr

gFrameErorr

2. h264_slice.c

add

#include "libavformat/avformat.h"

gFrameErorr

3. avformat.h

gFrameErorr

4.rtpdec.c

gFrameErorr

5. udp.c

UDP_MAX_PKT_SIZE

6. ff_ffplay.c

static int packet_queue_get_or_buffering(FFPlayer *ffp, PacketQueue *q, AVPacket *pkt, int *serial, int *finished)
{
   assert(finished);
   if (!ffp->packet_buffering)
       return packet_queue_get(q, pkt, 1, serial);
   
   while (1) {
       int new_packet = packet_queue_get(q, pkt, 0, serial);
       if (new_packet < 0)
           return -1;
       else if (new_packet == 0) {
           // if (q->is_buffer_indicator && !*finished)
           //  ffp_toggle_buffering(ffp, 1);
           av_log(NULL, AV_LOG_ERROR, "%s magical update packet_queue_get_or_buffering\n", __func__);
           new_packet = packet_queue_get(q, pkt, 1, serial);
           if (new_packet < 0)
               return -1;
       }
       
       if (*finished == *serial) {
           av_packet_unref(pkt);
           continue;
       }
       else
           break;
   }
   
   return 1;
}
static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
    /*
     if (vp->serial == nextvp->serial) {
     double duration = nextvp->pts - vp->pts;
     if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
     return vp->duration;
     else
     return duration;
     } else {
     return 0.0;
     }*/
    return vp->duration; //magical add
}

delay = 0

截屏2024-10-30 14.33.22.png

截屏2024-10-30 14.33.57.png

截屏2024-10-30 14.34.52.png

设置丢弃 GOP 的标志

ff_ffplay_def.h

discard_gop

static void decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_cond *empty_queue_cond) {
    memset(d, 0, sizeof(Decoder));
    d->avctx = avctx;
    d->queue = queue;
    d->empty_queue_cond = empty_queue_cond;
    d->start_pts = AV_NOPTS_VALUE;
    
    d->first_frame_decoded_time = SDL_GetTickHR();
    d->first_frame_decoded = 0;
//  d->avctx->flags |= CODEC_FLAG_LOW_DELAY; //magical add
    
    SDL_ProfilerReset(&d->decode_profiler, -1);
}

截屏2024-10-30 14.40.36.png

调用

 /*
    //设置使用HEVC(High Efficiency Video Coding)硬件解码器。HEVC是一种视频编码标准,允许更高的视频质量和更低的比特率。
    player.setOption(FijkOption.playerCategory, "mediacodec-hevc", 1);
    //启用帧丢弃,允许在视频渲染过程中丢弃一些帧,以减少延迟。 视频帧处理不过来的时候丢弃一些帧达到同步的效果
    player.setOption(FijkOption.playerCategory, "framedrop", 2);
    //禁用OpenSL ES音频渲染器。
    player.setOption(FijkOption.playerCategory, "opensles", 0);
    // packet-buffering: 禁用数据包缓冲。
    player.setOption(FijkOption.playerCategory, "packet-buffering", 0);
    // mediacodec-auto-rotate: 禁用自动旋转。
    player.setOption(FijkOption.playerCategory, "mediacodec-auto-rotate", 0);
    //设置最小帧数。可能与视频渲染有关。
    player.setOption(FijkOption.playerCategory, "min-frames", 2);
    //max_cached_duration: 设置最大缓存持续时间。
    player.setOption(FijkOption.playerCategory, "max_cached_duration", 0); //300
    //启用无限缓冲。
    player.setOption(FijkOption.playerCategory, "infbuf", 0);
    //设置重新连接次数。
    player.setOption(FijkOption.playerCategory, "reconnect", 5);
    //设置RTSP传输方式为TCP。
    player.setOption(FijkOption.formatCategory, "rtsp_transport", 'tcp');
    //设置RTSP标志为"prefer_tcp",首选使用TCP传输。
    player.setOption(FijkOption.formatCategory, "rtsp_flags", "prefer_tcp");
    //禁用HTTP检测范围支持
    player.setOption(FijkOption.formatCategory, "http-detect-range-support", 0);
    //设置分析持续时间。
    player.setOption(FijkOption.formatCategory, "analyzeduration", 1);
    player.setOption(FijkOption.formatCategory, "analyzemaxduration", 100);
    //设置缓冲区大小。
    player.setOption(FijkOption.formatCategory, "buffer_size", 1024 * 2);
    //设置最大帧速率。
    player.setOption(FijkOption.formatCategory, "max-fps", 0);
    //清除DNS缓存
    player.setOption(FijkOption.formatCategory, "dns_cache_clear", 1);
    //启用刷新数据包。
    player.setOption(FijkOption.formatCategory, "flush_packets", 1);
    //设置最大缓冲区大小。
    player.setOption(FijkOption.formatCategory, "max-buffer-size", 0);
    //设置文件标志为"nobuffer"
    player.setOption(FijkOption.formatCategory, "fflags", "nobuffer");
    // 设置探测文件的大小。播放前的探测Size,默认是1M, 改小一点会出画面更快
    player.setOption(FijkOption.formatCategory, "probesize", 100);
    //设置跳过环路滤波器 设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小
    player.setOption(FijkOption.codecCategory, "skip_loop_filter", 48);
    //设置跳过帧
    player.setOption(FijkOption.codecCategory, "skip_frame", 1);

    // 需要准备好后自动播放
    player.setOption(FijkOption.playerCategory, "start-on-prepared", 1);
    // 不额外优化(使能非规范兼容优化,默认值0 )
    player.setOption(FijkOption.playerCategory, "fast", 1);
     */

void setPlayerOptions() {
    player.setOption(FijkOption.playerCategory, "mediacodec-hevc", 1);
    player.setOption(FijkOption.playerCategory, "framedrop", 1);
    player.setOption(FijkOption.playerCategory, "start-on-prepared", 0);
    player.setOption(FijkOption.playerCategory, "opensles", 0);
    player.setOption(FijkOption.playerCategory, "mediacodec", 0);
    player.setOption(FijkOption.playerCategory, "start-on-prepared", 1);
    player.setOption(FijkOption.playerCategory, "packet-buffering", 0);
    player.setOption(FijkOption.playerCategory, "mediacodec-auto-rotate", 0);
    player.setOption(
        FijkOption.playerCategory, "mediacodec-handle-resolution-change", 0);
    player.setOption(FijkOption.playerCategory, "min-frames", 2);
    player.setOption(FijkOption.playerCategory, "max_cached_duration", 3);
    player.setOption(FijkOption.playerCategory, "infbuf", 1);
    player.setOption(FijkOption.playerCategory, "reconnect", 5);
    player.setOption(FijkOption.playerCategory, "framedrop", 5);

    player.setOption(FijkOption.formatCategory, "rtsp_transport", 'tcp');
    player.setOption(FijkOption.formatCategory, "http-detect-range-support", 0);
    player.setOption(FijkOption.formatCategory, "analyzeduration", 1);
    player.setOption(FijkOption.formatCategory, "rtsp_flags", "prefer_tcp");
    player.setOption(FijkOption.formatCategory, "buffer_size", 1024 * 5);
    player.setOption(FijkOption.formatCategory, "max-fps", 0);
    player.setOption(FijkOption.formatCategory, "analyzemaxduration", 20);
    // player.setOption(FijkOption.formatCategory, "analyzemaxduration", 50);
    player.setOption(FijkOption.formatCategory, "dns_cache_clear", 1);
    player.setOption(FijkOption.formatCategory, "flush_packets", 1);
    player.setOption(FijkOption.formatCategory, "max-buffer-size", 0);
    player.setOption(FijkOption.formatCategory, "fflags", "nobuffer");
    // player.setOption(FijkOption.formatCategory, "probesize", 200);
    player.setOption(FijkOption.formatCategory, "probesize", 100);
    player.setOption(FijkOption.formatCategory, "http-detect-range-support", 0);

    player.setOption(FijkOption.codecCategory, "skip_loop_filter", 48);
    player.setOption(FijkOption.codecCategory, "skip_frame", 0);
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容