步骤按照Bilibili/ijkplayer中的要求来:
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-ios
cd ijkplayer-ios
git checkout -B latest k0.8.8
./init-ios.sh
cd ios
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
# Demo
# open ios/IJKMediaDemo/IJKMediaDemo.xcodeproj with Xcode
#
# Import into Your own Application
# Select your project in Xcode.
# File -> Add Files to ... -> Select ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj
# Select your Application's target.
# Build Phases -> Target Dependencies -> Select IJKMediaFramework
# Build Phases -> Link Binary with Libraries -> Add:
# IJKMediaFramework.framework
#
# AudioToolbox.framework
# AVFoundation.framework
# CoreGraphics.framework
# CoreMedia.framework
# CoreVideo.framework
# libbz2.tbd
# libz.tbd
# MediaPlayer.framework
# MobileCoreServices.framework
# OpenGLES.framework
# QuartzCore.framework
# UIKit.framework
# VideoToolbox.framework
#
# ... (Maybe something else, if you get any link error)
#
详细的过程可参考如下的文章:
运行demo过程中遇到的问题:
1.提示./libavutil/arm/asm.S:50:9: error: unknown directive
原因是:基于ijkplayer做的直播demo
报错原因是因为xcode对32位的支持弱化了,可以在compile-ffmpeg.sh里面删除armv7 FF_ALL_ARCHS_IOS8_SDK=“arm64 i386 x86_64”
再次执行 ./compile-ffmpeg.sh all
可参考:iOS - IJKPlayer二三事
#if DEBUG
// 设置报告日志
[IJKFFMoviePlayerController setLogReport:YES];
// 设置日志的级别为Debug
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
#else
// 设置报告日志
[IJKFFMoviePlayerController setLogReport:NO];
// 设置日志的级别为Debug
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
#endif
[IJKFFMoviePlayerController checkIfFFmpegVersionMatch:YES];
// IJKFFOptions 是对视频的配置信息
IJKFFOptions *options = [IJKFFOptions optionsByDefault];
[options setOptionIntValue:15 forKey:@"max-fps" ofCategory:kIJKFFOptionCategoryPlayer];
//地址:
// http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8
// rtmp://live.hkstv.hk.lxdns.com/live/hks
// rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov
NSURL *url = [NSURL URLWithString:@"rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"];
self.player = [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// 控制视频填充模式
self.player.scalingMode = IJKMPMovieScalingModeAspectFit;
self.player.view.frame = self.view.bounds;
self.player.shouldAutoplay = YES;
self.view.autoresizesSubviews = YES;
[self.view addSubview:self.player.view];
[self.player prepareToPlay];
IJK使用通知来反馈播放状态,通常使用的有如下的几个通知:
/// 准备播放
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loadStateDidChange:)
name:IJKMPMoviePlayerLoadStateDidChangeNotification
object:self.player];
/// 播放完成或者用户退出
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackFinish:)
name:IJKMPMoviePlayerPlaybackDidFinishNotification
object:self.player];
/// 准备开始播放了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mediaIsPreparedToPlayDidChange:)
name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:self.player];
/// 播放状态改变了
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackStateDidChange:)
name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
object:self.player];
IJKMPMoviePlayerLoadStateDidChangeNotification
返回的缓冲的状态,可通过player的loadState
属性获取,有如下的几个类型:
IJKMPMovieLoadStateUnknown
IJKMPMovieLoadStatePlayable
IJKMPMovieLoadStatePlaythroughOK
– 代表缓冲完成,可以在这里去移除加载视图(Playback will be automatically started in this state when shouldAutoplay is YES)IJKMPMovieLoadStateStalled
– 表示正在缓冲中,可以在这个状态下加入加载视图,缓冲动画等(Playback will be automatically paused in this state, if started)在ZFPlayer中,是这样使用的:
- (void)loadStateDidChange:(NSNotification*)notification {
IJKMPMovieLoadState loadState = self.player.loadState;
if ((loadState & IJKMPMovieLoadStatePlayable)) {
NSLog(@"加载状态变成了缓存数据足够开始播放,但是视频并没有缓存完全");
} else if ((loadState & IJKMPMovieLoadStatePlaythroughOK)) {
// 加载完成,即将播放,停止加载的动画,并将其移除
NSLog(@"加载状态变成了已经缓存完成,如果设置了自动播放, 会自动播放");
} else if ((loadState & IJKMPMovieLoadStateStalled)) {
// 可能由于网速不好等因素导致了暂停,重新添加加载的动画
NSLog(@"网速不好等因素导致了暂停");
} else {
NSLog(@"加载状态变成了未知状态");
}
}
IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
会在准备播放完毕后调用
// 准备开始播放了
- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification {
ZFPlayerLog(@"加载状态变成了已经缓存完成,如果设置了自动播放, 会自动播放");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.loadState = ZFPlayerLoadStatePlaythroughOK;
});
[self play];
self.muted = self.muted;
ZFPlayerLog(@"mediaIsPrepareToPlayDidChange");
if (self.playerPrepareToPlay) self.playerReadyToPlay(self, self.assetURL);
}
IJKMPMoviePlayerPlaybackStateDidChangeNotification
会返回播放状态,通过playbackState
获取播放的状态,有如下的值:
typedef NS_ENUM(NSInteger, IJKMPMoviePlaybackState) {
IJKMPMoviePlaybackStateStopped,
IJKMPMoviePlaybackStatePlaying,
IJKMPMoviePlaybackStatePaused,
IJKMPMoviePlaybackStateInterrupted,
IJKMPMoviePlaybackStateSeekingForward,
IJKMPMoviePlaybackStateSeekingBackward
};
ZFPlayer中实现如下:
// 播放状态改变
- (void)moviePlayBackStateDidChange:(NSNotification *)notification {
if (self.player.playbackState == IJKMPMoviePlaybackStatePlaying) {
// 视频开始播放的时候开启计时器
if (!self.timer) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(update) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
}
switch (self.player.playbackState) {
case IJKMPMoviePlaybackStateStopped: {
ZFPlayerLog(@"播放器的播放状态变了,现在是停止状态 %d: stoped", (int)_player.playbackState);
// 这里的回调也会来多次(一次播放完成, 会回调三次), 所以, 这里不设置
self.playState = ZFPlayerPlayStatePlayStopped;
}
break;
case IJKMPMoviePlaybackStatePlaying: {
ZFPlayerLog(@"播放器的播放状态变了,现在是播放状态 %d: playing", (int)_player.playbackState);
self.playState = ZFPlayerPlayStatePlaying;
if (self.seekTime) {
[self seekToTime:self.seekTime completionHandler:nil];
self.seekTime = 0; // 滞空, 防止下次播放出错
[self play];
}
}
break;
case IJKMPMoviePlaybackStatePaused: {
ZFPlayerLog(@"播放器的播放状态变了,现在是暂停状态 %d: paused", (int)_player.playbackState);
}
break;
case IJKMPMoviePlaybackStateInterrupted: {
ZFPlayerLog(@"播放器的播放状态变了,现在是中断状态 %d: interrupted", (int)_player.playbackState);
}
break;
case IJKMPMoviePlaybackStateSeekingForward: {
ZFPlayerLog(@"播放器的播放状态变了,现在是向前拖动状态:%d forward",(int)self.player.playbackState);
}
break;
case IJKMPMoviePlaybackStateSeekingBackward: {
ZFPlayerLog(@"放器的播放状态变了,现在是向后拖动状态 %d: backward", (int)_player.playbackState);
}
break;
default: {
ZFPlayerLog(@"播放器的播放状态变了,现在是未知状态 %d: unknown", (int)_player.playbackState);
}
break;
}
}
IJKMPMoviePlayerPlaybackDidFinishNotification
返回的是播放结束
/// 播放完成
- (void)moviePlayBackFinish:(NSNotification *)notification {
int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
switch (reason) {
case IJKMPMovieFinishReasonPlaybackEnded: {
ZFPlayerLog(@"playbackStateDidChange: 播放完毕: %d\n", reason);
self.playState = ZFPlayerPlayStatePlayStopped;
if (self.playerDidToEnd) self.playerDidToEnd(self);
}
break;
case IJKMPMovieFinishReasonUserExited: {
ZFPlayerLog(@"playbackStateDidChange: 用户退出播放: %d\n", reason);
}
break;
case IJKMPMovieFinishReasonPlaybackError: {
ZFPlayerLog(@"playbackStateDidChange: 播放出现错误: %d\n", reason);
self.playState = ZFPlayerPlayStatePlayFailed;
if (self.playerPlayFailed) self.playerPlayFailed(self, @(reason));
}
break;
default:
ZFPlayerLog(@"playbackPlayBackDidFinish: ???: %d\n", reason);
break;
}
}