ijkplayer笔记

article/2025/8/24 18:41:35

一、初始化

- (id)initWithContentURLString:(NSString *)aUrlStringwithOptions:(IJKFFOptions *)options
{if (aUrlString == nil)return nil;self = [super init];if (self) {ijkmp_global_init();ijkmp_global_set_inject_callback(ijkff_inject_callback);[IJKFFMoviePlayerController checkIfFFmpegVersionMatch:NO];if (options == nil)options = [IJKFFOptions optionsByDefault];// IJKFFIOStatRegister(IJKFFIOStatDebugCallback);// IJKFFIOStatCompleteRegister(IJKFFIOStatCompleteDebugCallback);// init fields_scalingMode = IJKMPMovieScalingModeAspectFit;_shouldAutoplay = YES;memset(&_asyncStat, 0, sizeof(_asyncStat));memset(&_cacheStat, 0, sizeof(_cacheStat));_monitor = [[IJKFFMonitor alloc] init];// init media resource_urlString = aUrlString;// init player_mediaPlayer = ijkmp_ios_create(media_player_msg_loop);_msgPool = [[IJKFFMoviePlayerMessagePool alloc] init];IJKWeakHolder *weakHolder = [IJKWeakHolder new];weakHolder.object = self;ijkmp_set_weak_thiz(_mediaPlayer, (__bridge_retained void *) self);ijkmp_set_inject_opaque(_mediaPlayer, (__bridge_retained void *) weakHolder);ijkmp_set_ijkio_inject_opaque(_mediaPlayer, (__bridge_retained void *)weakHolder);ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "start-on-prepared", _shouldAutoplay ? 1 : 0);// init video sink_glView = [[IJKSDLGLView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];_glView.shouldShowHudView = NO;_view   = _glView;[_glView setHudValue:nil forKey:@"scheme"];[_glView setHudValue:nil forKey:@"host"];[_glView setHudValue:nil forKey:@"path"];[_glView setHudValue:nil forKey:@"ip"];[_glView setHudValue:nil forKey:@"tcp-info"];[_glView setHudValue:nil forKey:@"http"];[_glView setHudValue:nil forKey:@"tcp-spd"];[_glView setHudValue:nil forKey:@"t-prepared"];[_glView setHudValue:nil forKey:@"t-render"];[_glView setHudValue:nil forKey:@"t-preroll"];[_glView setHudValue:nil forKey:@"t-http-open"];[_glView setHudValue:nil forKey:@"t-http-seek"];self.shouldShowHudView = options.showHudView;ijkmp_ios_set_glview(_mediaPlayer, _glView);ijkmp_set_option(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");
#ifdef DEBUG[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
#else[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];
#endif// init audio sink[[IJKAudioKit sharedInstance] setupAudioSession];[options applyTo:_mediaPlayer];_pauseInBackground = NO;// init extra_keepScreenOnWhilePlaying = YES;[self setScreenOn:YES];_notificationManager = [[IJKNotificationManager alloc] init];[self registerApplicationObservers];}return self;
}

二、播放
prepareToPlay为native方法,映射为ijkmp_prepare_async,经过一系列调用后会走到ijkplayer.c的ijkmp_prepare_async_l方法里面:

    ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING);msg_queue_start(&mp->ffplayer->msg_queue);// released in msg_loopijkmp_inc_ref(mp);mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");// msg_thread is detached inside msg_loop// TODO: 9 release weak_thiz if pthread_create() failed;int retval = ffp_prepare_async_l(mp->ffplayer, mp->data_source);if (retval < 0) {ijkmp_change_state_l(mp, MP_STATE_ERROR);return retval;}return 0;

这里显示启动了一个队列,然后启动了一个loop消息线程(一个独立的线程来处理消息派发),然后走了一个关键函数ffp_prepare_async_l。重点看下面的ffp_prepare_async_l,这个才是进入到准备播放的阶段:

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 rtmpav_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");ffp_show_version_str(ffp, "ijkplayer",      ijk_version_info());ffp_show_version_str(ffp, "FFmpeg",         av_version_info());ffp_show_version_int(ffp, "libavutil",      avutil_version());ffp_show_version_int(ffp, "libavcodec",     avcodec_version());ffp_show_version_int(ffp, "libavformat",    avformat_version());ffp_show_version_int(ffp, "libswscale",     swscale_version());ffp_show_version_int(ffp, "libswresample",  swresample_version());av_log(NULL, AV_LOG_INFO, "===== options =====\n");ffp_show_dict(ffp, "player-opts", ffp->player_opts);ffp_show_dict(ffp, "format-opts", ffp->format_opts);ffp_show_dict(ffp, "codec-opts ", ffp->codec_opts);ffp_show_dict(ffp, "sws-opts   ", ffp->sws_dict);ffp_show_dict(ffp, "swr-opts   ", ffp->swr_opts);av_log(NULL, AV_LOG_INFO, "===================\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;}#if CONFIG_AVFILTERif (ffp->vfilter0) {GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);ffp->vfilters_list[ffp->nb_vfilters - 1] = ffp->vfilter0;}
#endifVideoState *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;
}

前面判断直播视频流协议rtmp或rtsp,再后面基本是输出信息的,真正的核心函数是stream_open。这个才是根据地址打开视频流,代码如下:

static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
{assert(!ffp->is);VideoState *is;is = av_mallocz(sizeof(VideoState));if (!is)return NULL;is->filename = av_strdup(filename);if (!is->filename)goto fail;is->iformat = iformat;is->ytop    = 0;is->xleft   = 0;
#if defined(__ANDROID__)if (ffp->soundtouch_enable) {is->handle = ijk_soundtouch_create();}
#endif/* start video display */if (frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1) < 0)goto fail;if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)goto fail;if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)goto fail;if (packet_queue_init(&is->videoq) < 0 ||packet_queue_init(&is->audioq) < 0 ||packet_queue_init(&is->subtitleq) < 0)goto fail;if (!(is->continue_read_thread = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());goto fail;}if (!(is->video_accurate_seek_cond = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());ffp->enable_accurate_seek = 0;}if (!(is->audio_accurate_seek_cond = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());ffp->enable_accurate_seek = 0;}init_clock(&is->vidclk, &is->videoq.serial);init_clock(&is->audclk, &is->audioq.serial);init_clock(&is->extclk, &is->extclk.serial);is->audio_clock_serial = -1;if (ffp->startup_volume < 0)av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", ffp->startup_volume);if (ffp->startup_volume > 100)av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", ffp->startup_volume);ffp->startup_volume = av_clip(ffp->startup_volume, 0, 100);ffp->startup_volume = av_clip(SDL_MIX_MAXVOLUME * ffp->startup_volume / 100, 0, SDL_MIX_MAXVOLUME);is->audio_volume = ffp->startup_volume;is->muted = 0;is->av_sync_type = ffp->av_sync_type;is->play_mutex = SDL_CreateMutex();is->accurate_seek_mutex = SDL_CreateMutex();ffp->is = is;is->pause_req = !ffp->start_on_prepared;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->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());
fail:is->abort_request = true;if (is->video_refresh_tid)SDL_WaitThread(is->video_refresh_tid, NULL);stream_close(ffp);return NULL;}return is;
}

这里可以看到有3个队列初始化,分别是视频、音频和字幕,这些队列通过frame_queue_init又分别分为2个队列,原始的和解码后的。再往下看就是2个线程的创建,分别是video_refresh_thread和read_thread,从字面上理解,应当是输出刷新视频和读取线程。我们下面先从读取线程入手,整个代码很长,注意这里的ic变量。是个AVFormatContext类型,这里放的是流的信息和数据。

read_thread:

......
ic = avformat_alloc_context();
......
err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);
......
err = avformat_find_stream_info(ic, opts);
......
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 h264if (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;}}}
....../* open the streams */if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);}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]);}
....../* offset should be seeked*/if (ffp->seek_at_start > 0) {ffp_seek_to_l(ffp, ffp->seek_at_start);}for (;;) {......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);}}......

avformat_alloc_context是分配空间初始化。然后在avformat_open_input里面填充ic,这个函数里要读取网络数据包的信息,并判定格式等等操作。这个for循环取出每一帧,然后进行判断类型,音视频字幕归类,并分别设置个数。再来进入到核心的一个函数,就是stream_component_open,这里根据不同类型的帧是否有来进行流的读取及解码工作。真正的读取在av_read_frame,读取解码后的数据,读取到pkt里。这个过程是在一个for无限循环内部进行的,在seek的处理之后进行。这个for不小心会漏看的…那么这个av_read_frame读取到的是什么数据呢?看结构AVPacket,这个的说明已经挺清晰了,是一个压缩的数据帧,是否是关键帧已经在flags里标记了。无论这个循环前后干了什么,都是要走这一步,读取数据帧。下面有一大段是对错误的处理,暂时略过。下面有一段检查数据包是否在用户指定的播放范围内,并根据时间戳排队,否则丢弃的过程。从stream_start_time=…开始,我理解的是计算出当前数据帧的时间戳后再计算出播放的起始时间到当前时间,然后看这个时间戳是否在此范围内。范围内的就put到队列中,否则丢弃。

三、流程图

ijkplayer流程.png


http://chatgpt.dhexx.cn/article/VU9ddacI.shtml

相关文章

Ijkplayer编译

记录一下编译ijkplayer过程 一、环境 1.1、VMware ubuntu 1.2、安装git、vim sudo apt install git sudo apt install vim二、配置编译环境 2.1、配置AndroidSDK环境 2.1.1、下载AndroidSDK wget http://dl.google.com/android/android-sdk_r24.2-linux.tgz国内下载地址…

ijkplayer播放器剖析(一)让ijkplayer播起来

一、引言&#xff1a; ijkplayer是一款对FFmpeg封装非常好的第三方开源播放器&#xff0c;遗憾的是&#xff0c;ijkplayer2.0似乎不开源&#xff0c;并且1.0版本更新也基本停止了&#xff0c;很多公司都会采用ijkplayer作为其播放应用的内核&#xff0c;这款集合软硬件编解码功…

13_android编译ijkplayer

13_android编译ijkplayer 一.编译环境 macOS Big Sur 11.4NDK r10eHomeBrewgit 二.编译前准备 配置ANDROID_SDK和ANDROID_NDK环境变量 安装git&#xff0c;make&#xff0c; yasm brew install git brew install make brew install yasm三.使用git获取ijkplayer最新源码 g…

ijkplayer项目

ijkplayer项目 环境配置 NDK全称&#xff1a;Native Development Kit。 1、NDK是一系列工具的集合。NDK提供了一系列的工具&#xff0c;帮助开发者快速开发C&#xff08;或C&#xff09;的动态库&#xff0c;并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨…

ijkplayer android 内存,IjkPlayer

ijkplayer是b站开源的超级好用的视频播放器&#xff0c;小编这里为大家送上。相信大家都早有耳闻。ijkplayer Android和ios都可用&#xff0c;还支持多种视频的硬解码。人生苦短&#xff0c;不如快点试一试。需要的朋友快来西西下载吧&#xff01; 应用简介 ijkplayer是Bilibil…

android集成 ijkplayer Ijkplayer集成使用方案 + demo

首先想使用ijkplayer的话&#xff0c;肯定得先编译啦&#xff0c;但是我之前已经编译好了&#xff0c;时间是2018年3月份左右&#xff0c;版本也是当时最新的0.8.8&#xff0c;所以大可放心使用 其实拉到项目中还是很简单的。也就是把要适配的.so库拉到项目中&#xff0c;再把需…

ijkplayer 源码分析(上)

本文基于0.8.8版本的 ijkplayer &#xff0c;对其源码进行剖析&#xff0c;涉及到不同平台下的封装接口或处理方式时&#xff0c;均以 Android 为例。 ijkplayer 是一款比较出众的开源 Android/IOS 跨平台播放器&#xff0c;基于 ffplay&#xff0c;API 易于集成&#xff0c;可…

ijkplayer播放器剖析(五)视频同步与渲染机制分析

ijkplayer播放器剖析系列文章&#xff1a; kplayer播放器剖析&#xff08;一&#xff09;从应用层分析至Jni层的流程分析 ijkplayer播放器剖析&#xff08;二&#xff09;消息机制分析 ijkplayer播放器剖析&#xff08;三&#xff09;音频解码与音频输出机制分析 ijkplay…

ijkplayer播放器详解使用教程

1.认识ijkplayer 最近公司准备开发一款视频播放及直播的应用&#xff0c;找了许多开源的框架&#xff0c;大部分都是基于ffmpeg开发的。最开始准备用Vitamio框架开发的&#xff0c;相关的文章也比较丰富&#xff0c;结果对于非个人移动应用均需购买Vitamio使用授权。不过B站开…

ijkplayer播放器架构从原型到升级

ijkplayer是一款跨平台的播放器&#xff0c;支持Android与iOS端&#xff0c;核心部分基于ffmpeg&#xff0c;支持Android的mediacodec硬解与iOS的videotoolbox硬解&#xff0c;视频图像采用OpenGL进行渲染。许多主流播放器都使用ijkplayer作为播放方案。接下来我们从核心播放流…

游戏手柄改typec接口

前言 几年前买的杂牌游戏手柄坏了好久&#xff0c;连接十分不稳定&#xff0c;稍微动一下线就会断连&#xff0c;我推断是线的质量不好&#xff0c;理论上将把线换掉应该就好了。一不做二不休&#xff0c;我想直接改成typec接口&#xff0c;手柄与数据线分离&#xff0c;这样就…

nl80211_iftype接口类型详解

NL80211_IFTYPE_UNSPECIFIED 上层协议未指定硬件接口类型&#xff0c;由驱动指定 NL80211_IFTYPE_ADHOC independent BSS member&#xff0c;各个无线主机之间对等交换数据 NL80211_IFTYPE_STATION managed BSS member NL80211_IFTYPE_AP 接入点&#xff0c;通常一端通过有…

android接口和type c对比,USB Type-C究竟比3.5mm音频接口好在哪里?

随着苹果宣布将在iPhone7/7 Plus上取消传统的3.5mm音频接口,不少动作快的手机厂商已经即使跟进了,目前乐视所有新机和摩托罗拉Moto Z均使用USB Type-C接口代替3.5mm音频接口,这样做究竟有什么好处呢? 基础知识 首先我们需要知道,3.5mm接口所传输的是立体声模拟音频,这也就…

TYPE-C接口的定义诠释以及功能参数挖掘

现在的安卓手机大多都采用了Type-C接口&#xff0c;如华为、荣耀、小米、三星、魅族等。对于这个接口&#xff0c;大家不再陌生&#xff0c;但大多数人对它的认识还停留在 “支持正反插”、“用来充电” 的基础层面。今天就来深入挖掘一下TYPE-C发展七年至今&#xff0c;为何能…

Type-C接口原理图,附引脚说明

维基链接&#xff1a;https://zh.wikipedia.org/wiki/USB_Type-C 针名描述针名描述A1GND接地B12GND接地A2SSTXp1SuperSpeed差分信号#1&#xff0c;TX&#xff0c;正B11SSRXp1SuperSpeed差分信号#1&#xff0c;RX&#xff0c;正A3SSTXn1SuperSpeed差分信号#1&#xff0c;TX&…

typec接口知识

typec接口定义(24脚) typec母头 typec公头(一对D,D-,只有一个CC脚&#xff0c;另一个CC脚变成了VCONN) D/D-&#xff1a;当USB3接口不可用的时候&#xff0c;这些引脚为USB2信号提供信号通道。 Vbus/GND&#xff1a;这些引脚能够为上行数据接口提供100W的供电能力&#xff0c;…

深入学习USB(6)USB Type-C接口定义概念解析

文章目录 **一、Usb type c接口定义介绍****二、USB3.1 type-c接口特性****三、USB Type-C接口的优点****四、usb type-c接引脚定义****五、usb type-c接口主要功能****六、usb type-c接口工作流程**七、市场上Type-C/PD有哪几种分类呢&#xff1f; 一、Usb type c接口定义介绍…

lightning接口_苹果Lightning接口与Type-C接口,谁更胜一筹?

曾几何时,在苹果的强势助攻下,Lightning接口在数码界曾一度混得风生水起!然而,近年来随着智能手机的发展,手机接口快速升级,其中Type-C接口凭借更快的数据传输、充电速度等优势,越来越多的出现在大家的视线中,隐隐盖过Lightning接口的风头,就连特立独行的苹果也对其青…

Type-C接口技术(一)

前言 电子产品接口的迅速发展是电子技术迅速发展的一个缩影&#xff0c;随着现在电子产品对数据传输和充电功率要求越来越高的情况下&#xff0c;一些以前常用接口开始慢慢被淘汰&#xff0c;而支持更高速率和充电功率的的Type-C接口慢慢开始大放异彩&#xff0c;此外支持盲插…

浅谈一下Type-C接口发展历程

1996年&#xff0c;由英特尔、微软、ibm等多家公司联合设计的usb标准问世&#xff0c;键盘、鼠标、智能手机以及打印机等等大多使用usb标准来实现供电和数据传输。 usb接口从诞生之初就是为了实现通用这个目的。在usb诞生之前&#xff0c;键盘、鼠标多使用ps二接口&#xff0c;…