Android IjkPlayer解决RTSP延时300ms左右
简介
在上一篇《ijkplayer编译-RTSP》中介绍了,ijkplayer如何进行编译成so库的,以及如何开启rtsp。那么实际在使用的时候会发现延迟不是一般的大。
现在来介绍一下如何解决这个延迟的,经过测试延迟时间可以控制在500ms左右。
修改ijkplayer源码的ff_ffplay.c文件
路径是在ijkplayer-android/ijkmedia/ijkplayer/ff_ffplay.c
修改其中packet_queue_get_or_buffering
方法 如下:
注意:注释掉的是原来的代码。
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); int new_packet = packet_queue_get(q, pkt, 1, serial); if (new_packet < 0) { new_packet = packet_queue_get(q, pkt, 0, serial); if(new_packet < 0) return -1; // return -1; }else if (new_packet == 0) { // if (q->is_buffer_indicator && !*finished) if (!finished) ffp_toggle_buffering(ffp, 1); 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; }
修改此处方法编译测试发现视频播放还是存在一秒左右的延时,达不到预计效果,则继续修改vp_duration
方法 ,如下:
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; }
直接return vp->duration;
,在修改ffplay_video_thread
方法:
static int ffplay_video_thread(void *arg) { FFPlayer *ffp = arg; VideoState *is = ffp->is; AVFrame *frame = av_frame_alloc(); double pts; double duration; int ret; AVRational tb = is->video_st->time_base; // AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL); int64_t dst_pts = -1; int64_t last_dst_pts = -1; int retry_convert_image = 0; int convert_frame_count = 0; #if CONFIG_AVFILTER AVFilterGraph *graph = avfilter_graph_alloc(); AVFilterContext *filt_out = NULL, *filt_in = NULL; int last_w = 0; int last_h = 0; enum AVPixelFormat last_format = -2; int last_serial = -1; int last_vfilter_idx = 0; if (!graph) { av_frame_free(&frame); return AVERROR(ENOMEM); } #else ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, ffp_get_video_rotate_degrees(ffp)); #endif if (!frame) { #if CONFIG_AVFILTER avfilter_graph_free(&graph); #endif return AVERROR(ENOMEM); } for (;;) { ret = get_video_frame(ffp, frame); if (ret < 0) goto the_end; if (!ret) continue; if (ffp->get_frame_mode) { if (!ffp->get_img_info || ffp->get_img_info->count <= 0) { av_frame_unref(frame); continue; } last_dst_pts = dst_pts; if (dst_pts < 0) { dst_pts = ffp->get_img_info->start_time; } else { dst_pts += (ffp->get_img_info->end_time - ffp->get_img_info->start_time) / (ffp->get_img_info->num - 1); } pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); pts = pts * 1000; if (pts >= dst_pts) { while (retry_convert_image <= MAX_RETRY_CONVERT_IMAGE) { ret = convert_image(ffp, frame, (int64_t)pts, frame->width, frame->height); if (!ret) { convert_frame_count++; break; } retry_convert_image++; av_log(NULL, AV_LOG_ERROR, "convert image error retry_convert_image = %d\n", retry_convert_image); } retry_convert_image = 0; if (ret || ffp->get_img_info->count <= 0) { if (ret) { av_log(NULL, AV_LOG_ERROR, "convert image abort ret = %d\n", ret); ffp_notify_msg3(ffp, FFP_MSG_GET_IMG_STATE, 0, ret); } else { av_log(NULL, AV_LOG_INFO, "convert image complete convert_frame_count = %d\n", convert_frame_count); } goto the_end; } } else { dst_pts = last_dst_pts; } av_frame_unref(frame); continue; } #if CONFIG_AVFILTER if ( last_w != frame->width || last_h != frame->height || last_format != frame->format || last_serial != is->viddec.pkt_serial || ffp->vf_changed || last_vfilter_idx != is->vfilter_idx) { SDL_LockMutex(ffp->vf_mutex); ffp->vf_changed = 0; av_log(NULL, AV_LOG_DEBUG, "Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n", last_w, last_h, (const char *)av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial, frame->width, frame->height, (const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), is->viddec.pkt_serial); avfilter_graph_free(&graph); graph = avfilter_graph_alloc(); if ((ret = configure_video_filters(ffp, graph, is, ffp->vfilters_list ? ffp->vfilters_list[is->vfilter_idx] : NULL, frame)) < 0) { // FIXME: post error SDL_UnlockMutex(ffp->vf_mutex); goto the_end; } filt_in = is->in_video_filter; filt_out = is->out_video_filter; last_w = frame->width; last_h = frame->height; last_format = frame->format; last_serial = is->viddec.pkt_serial; last_vfilter_idx = is->vfilter_idx; // frame_rate = av_buffersink_get_frame_rate(filt_out); SDL_UnlockMutex(ffp->vf_mutex); } ret = av_buffersrc_add_frame(filt_in, frame); if (ret < 0) goto the_end; while (ret >= 0) { is->frame_last_returned_time = av_gettime_relative() / 1000000.0; ret = av_buffersink_get_frame_flags(filt_out, frame, 0); if (ret < 0) { if (ret == AVERROR_EOF) is->viddec.finished = is->viddec.pkt_serial; ret = 0; break; } is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time; if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) is->frame_last_filter_delay = 0; tb = av_buffersink_get_time_base(filt_out); #endif //duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); duration = 0.01;//直接修改duration 等于0.01即可 pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); ret = queue_picture(ffp, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); av_frame_unref(frame); #if CONFIG_AVFILTER } #endif if (ret < 0) goto the_end; } the_end: #if CONFIG_AVFILTER avfilter_graph_free(&graph); #endif av_log(NULL, AV_LOG_INFO, "convert image convert_frame_count = %d\n", convert_frame_count); av_frame_free(&frame); return 0; }
到此修改完毕
编译测试播放视频延时在500ms左右
编译的so库以及修改的配置文件和代码下载:
https://github.com/itzuo/ijkplayer-android
分类:
ijkplayer
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库