From 4eb106a7682f7a4513280158b8445acb3285f169 Mon Sep 17 00:00:00 2001 From: zccicy Date: Wed, 7 Jan 2015 11:23:03 +0800 Subject: [PATCH] videotoolbox support --- .../IJKMediaDemo.xcodeproj/project.pbxproj | 8 + .../IJKMoviePlayerViewController.m | 2 +- .../IJKMediaPlayer.xcodeproj/project.pbxproj | 40 + .../IJKFFMoviePlayerController.m | 2 +- .../IJKMediaPlayer/IJKFFOptions.h | 10 +- .../IJKMediaPlayer/IJKFFOptions.m | 18 +- .../IJKMediaPlayer/h264_sps_parser.h | 260 ++++++ .../ijkmedia/ijkplayer/ios/ijkplayer_ios.h | 2 + .../ijkmedia/ijkplayer/ios/ijkplayer_ios.m | 43 +- .../ijkplayer/ios/pipeline/IJKVideoToolBox.h | 83 ++ .../ijkplayer/ios/pipeline/IJKVideoToolBox.m | 774 ++++++++++++++++++ .../ijkplayer/ios/pipeline/ffpipeline_ios.c | 100 +++ .../ijkplayer/ios/pipeline/ffpipeline_ios.h | 36 + .../ffpipenode_ios_videotoolbox_vdec.h | 32 + .../ffpipenode_ios_videotoolbox_vdec.m | 185 +++++ .../ffpipenode_ios_videotoolbox_vout.h | 32 + .../ffpipenode_ios_videotoolbox_vout.m | 55 ++ .../ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.h | 4 +- .../ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.m | 15 +- .../ijksdl/ios/ijksdl_vout_ios_gles2.m | 12 +- .../ios/ijksdl_vout_overlay_videotoolbox.h | 35 + .../ios/ijksdl_vout_overlay_videotoolbox.m | 145 ++++ 22 files changed, 1869 insertions(+), 24 deletions(-) create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/h264_sps_parser.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.m create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.c create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.m create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.m create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.h create mode 100644 ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.m diff --git a/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj b/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj index 14a59609..e0954aed 100644 --- a/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj +++ b/ios/IJKMediaDemo/IJKMediaDemo.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 45D57D611A53233200BDD389 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45D57D601A53233200BDD389 /* CoreVideo.framework */; }; + 45D57D631A53233800BDD389 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45D57D621A53233800BDD389 /* VideoToolbox.framework */; }; E60E8C2A19EF70BB005B5B6E /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E60E8C2919EF70BB005B5B6E /* CoreMedia.framework */; }; E60FFBE418BF695700825D7F /* libIJKMediaPlayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E6D74F2E18A5F94B00165BFD /* libIJKMediaPlayer.a */; }; E612EAE517F7E0F800BEE660 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E612EAE417F7E0F800BEE660 /* MediaPlayer.framework */; }; @@ -58,6 +60,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 45D57D601A53233200BDD389 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + 45D57D621A53233800BDD389 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; E60E8C2719EF70A7005B5B6E /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; E60E8C2919EF70BB005B5B6E /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; E612EAE417F7E0F800BEE660 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; @@ -103,6 +107,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 45D57D631A53233800BDD389 /* VideoToolbox.framework in Frameworks */, + 45D57D611A53233200BDD389 /* CoreVideo.framework in Frameworks */, E60E8C2A19EF70BB005B5B6E /* CoreMedia.framework in Frameworks */, E61B45AE19EF7021002792EC /* AVFoundation.framework in Frameworks */, E60FFBE418BF695700825D7F /* libIJKMediaPlayer.a in Frameworks */, @@ -166,6 +172,8 @@ E6903EFE17EAF70200CFD954 /* Frameworks */ = { isa = PBXGroup; children = ( + 45D57D621A53233800BDD389 /* VideoToolbox.framework */, + 45D57D601A53233200BDD389 /* CoreVideo.framework */, E63FC29917F062E8003551EB /* libbz2.dylib */, E63FC29517F060A7003551EB /* libz.dylib */, E63FC28F17F04C83003551EB /* AudioToolbox.framework */, diff --git a/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m b/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m index 941fed2a..904e1517 100644 --- a/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m +++ b/ios/IJKMediaDemo/IJKMediaDemo/IJKMoviePlayerViewController.m @@ -39,7 +39,7 @@ [[UIApplication sharedApplication] setStatusBarHidden:YES]; [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:NO]; - NSURL *theMovieURL = [NSURL URLWithString:@"http://wshdl.acgvideo.com/live/live_5099_3038_d0ffe541.flv"]; + NSURL *theMovieURL = [NSURL URLWithString:@"http://vhotwsh.video.qq.com/flv/32/146/r0015d10iwu.p203.1.mp4?vkey=68023055CEC9CC9E1D988B0E71E4B58C38B0374F1CB9805F4A1901D8E87B42137DD9680CF88D3685C3A59F030BAE55AA2E7238551DCB6BFF&fmt=sd&type=mp4"]; [IJKFFMoviePlayerController setLogReport:YES]; self.player = [[IJKFFMoviePlayerController alloc] initWithContentURL:theMovieURL withOptions:nil]; diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj b/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj index c0e1b88c..c43d971c 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj +++ b/ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj/project.pbxproj @@ -8,6 +8,11 @@ /* Begin PBXBuildFile section */ 451103131A5CE36A00337D24 /* IJKSDLGLRenderNV12.m in Sources */ = {isa = PBXBuildFile; fileRef = 451103121A5CE36A00337D24 /* IJKSDLGLRenderNV12.m */; }; + 454316261A66493700676070 /* ffpipeline_ios.c in Sources */ = {isa = PBXBuildFile; fileRef = 454316201A66493700676070 /* ffpipeline_ios.c */; }; + 454316271A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.m in Sources */ = {isa = PBXBuildFile; fileRef = 454316231A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.m */; }; + 454316281A66493700676070 /* ffpipenode_ios_videotoolbox_vout.m in Sources */ = {isa = PBXBuildFile; fileRef = 454316251A66493700676070 /* ffpipenode_ios_videotoolbox_vout.m */; }; + 4543162B1A66497900676070 /* IJKVideoToolBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 4543162A1A66497900676070 /* IJKVideoToolBox.m */; }; + 45DB4AAC1A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.m in Sources */ = {isa = PBXBuildFile; fileRef = 45DB4AA81A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.m */; }; E62139BE180FA89B00553533 /* IJKFFOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = E62139BD180FA89A00553533 /* IJKFFOptions.m */; }; E62139BF180FAE5F00553533 /* IJKFFOptions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = E62139BC180FA89A00553533 /* IJKFFOptions.h */; }; E63FC27117F01143003551EB /* ijksdl_audio.c in Sources */ = {isa = PBXBuildFile; fileRef = E63FC27017F01143003551EB /* ijksdl_audio.c */; }; @@ -87,6 +92,17 @@ /* Begin PBXFileReference section */ 451103111A5CE36A00337D24 /* IJKSDLGLRenderNV12.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJKSDLGLRenderNV12.h; sourceTree = ""; }; 451103121A5CE36A00337D24 /* IJKSDLGLRenderNV12.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJKSDLGLRenderNV12.m; sourceTree = ""; }; + 454316201A66493700676070 /* ffpipeline_ios.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ffpipeline_ios.c; path = ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.c; sourceTree = ""; }; + 454316211A66493700676070 /* ffpipeline_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ffpipeline_ios.h; path = ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.h; sourceTree = ""; }; + 454316221A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ffpipenode_ios_videotoolbox_vdec.h; path = ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.h; sourceTree = ""; }; + 454316231A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ffpipenode_ios_videotoolbox_vdec.m; path = ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.m; sourceTree = ""; }; + 454316241A66493700676070 /* ffpipenode_ios_videotoolbox_vout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ffpipenode_ios_videotoolbox_vout.h; path = ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.h; sourceTree = ""; }; + 454316251A66493700676070 /* ffpipenode_ios_videotoolbox_vout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ffpipenode_ios_videotoolbox_vout.m; path = ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.m; sourceTree = ""; }; + 454316291A66497900676070 /* IJKVideoToolBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJKVideoToolBox.h; path = ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.h; sourceTree = ""; }; + 4543162A1A66497900676070 /* IJKVideoToolBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJKVideoToolBox.m; path = ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.m; sourceTree = ""; }; + 45DB4AA71A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ijksdl_vout_overlay_videotoolbox.h; sourceTree = ""; }; + 45DB4AA81A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ijksdl_vout_overlay_videotoolbox.m; sourceTree = ""; }; + 45DCC8E51A66984200DE441F /* h264_sps_parser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h264_sps_parser.h; sourceTree = ""; }; E62139BC180FA89A00553533 /* IJKFFOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJKFFOptions.h; sourceTree = ""; }; E62139BD180FA89A00553533 /* IJKFFOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJKFFOptions.m; sourceTree = ""; }; E63FC27017F01143003551EB /* ijksdl_audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ijksdl_audio.c; sourceTree = ""; }; @@ -289,6 +305,22 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4543161F1A66481600676070 /* pipeline */ = { + isa = PBXGroup; + children = ( + 454316201A66493700676070 /* ffpipeline_ios.c */, + 454316211A66493700676070 /* ffpipeline_ios.h */, + 454316221A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.h */, + 454316231A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.m */, + 454316241A66493700676070 /* ffpipenode_ios_videotoolbox_vout.h */, + 454316251A66493700676070 /* ffpipenode_ios_videotoolbox_vout.m */, + 454316291A66497900676070 /* IJKVideoToolBox.h */, + 4543162A1A66497900676070 /* IJKVideoToolBox.m */, + 45DCC8E51A66984200DE441F /* h264_sps_parser.h */, + ); + name = pipeline; + sourceTree = ""; + }; E63FC27217F013DE003551EB /* dummy */ = { isa = PBXGroup; children = ( @@ -322,6 +354,7 @@ E66F8DF817EFEC1300354D80 /* ios */ = { isa = PBXGroup; children = ( + 4543161F1A66481600676070 /* pipeline */, E66F8E0117EFEEA400354D80 /* ijkplayer_ios.m */, E66F8E0217EFEEA400354D80 /* ijkplayer_ios.h */, ); @@ -662,6 +695,8 @@ E6EE92AA1878230C009EAB56 /* ijksdl_thread_ios.m */, E6EE92AB1878230C009EAB56 /* ijksdl_vout_ios_gles2.h */, E6EE92AC1878230C009EAB56 /* ijksdl_vout_ios_gles2.m */, + 45DB4AA71A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.h */, + 45DB4AA81A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.m */, E6EE92C618782770009EAB56 /* IJKSDLAudioKit.h */, E6EE92C718782770009EAB56 /* IJKSDLAudioKit.m */, E6EE92C01878236A009EAB56 /* IJKSDLAudioQueueController.h */, @@ -765,6 +800,7 @@ E6EE92C818782770009EAB56 /* IJKSDLAudioKit.m in Sources */, E690403117EAFC6100CFD954 /* ijksdl_stdinc.c in Sources */, E6EE92BC1878230C009EAB56 /* IJKSDLGLRenderI420.m in Sources */, + 45DB4AAC1A5D52AE005CAD41 /* ijksdl_vout_overlay_videotoolbox.m in Sources */, E690403217EAFC6100CFD954 /* ijksdl_thread.c in Sources */, E67B91AF1A3801DB00717EA9 /* ff_ffpipeline.c in Sources */, E67B91B01A3801DB00717EA9 /* ff_ffpipenode.c in Sources */, @@ -773,10 +809,12 @@ E690403417EAFC6100CFD954 /* ijksdl_vout.c in Sources */, E6EE92B91878230C009EAB56 /* ijksdl_thread_ios.m in Sources */, E66F8DC117EEC65200354D80 /* IJKMPMoviePlayerController.m in Sources */, + 4543162B1A66497900676070 /* IJKVideoToolBox.m in Sources */, E67C4E0819D15EEA00415CEE /* IJKAVMoviePlayerController.m in Sources */, E6FAD9571A515CE300725002 /* ijkmeta.c in Sources */, E6EE92B81878230C009EAB56 /* ijksdl_aout_ios_audiounit.m in Sources */, E6EE92C21878236A009EAB56 /* IJKSDLAudioQueueController.m in Sources */, + 454316271A66493700676070 /* ffpipenode_ios_videotoolbox_vdec.m in Sources */, E67B91BA1A3801E600717EA9 /* ffpipenode_ffplay_vout.c in Sources */, E66F8DE717EFD9C300354D80 /* IJKFFMoviePlayerController.m in Sources */, E66F8DF117EFEA9400354D80 /* ijkplayer.c in Sources */, @@ -794,9 +832,11 @@ E6EE92BA1878230C009EAB56 /* ijksdl_vout_ios_gles2.m in Sources */, E6716E4B1807E5FC00B3FBC1 /* IJKMediaUtils.m in Sources */, E6716E4E1807EA5000B3FBC1 /* IJKFFMrl.m in Sources */, + 454316281A66493700676070 /* ffpipenode_ios_videotoolbox_vout.m in Sources */, E67B91B91A3801E600717EA9 /* ffpipenode_ffplay_vdec.c in Sources */, 451103131A5CE36A00337D24 /* IJKSDLGLRenderNV12.m in Sources */, E62139BE180FA89B00553533 /* IJKFFOptions.m in Sources */, + 454316261A66493700676070 /* ffpipeline_ios.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m index 4f8c4c0a..c2c80983 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFMoviePlayerController.m @@ -440,7 +440,7 @@ inline static void fillMetaInternal(NSMutableDictionary *meta, IjkMediaMeta *raw const char *value = ijkmeta_get_string_l(rawMeta, name); if (value) { [meta setObject:[NSString stringWithUTF8String:value] forKey:key]; - } else if (defaultValue){ + } else if (defaultValue) { [meta setObject:defaultValue forKey:key]; } else { [meta removeObjectForKey:key]; diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.h b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.h index 29721acc..75771e85 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.h +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.h @@ -30,10 +30,12 @@ typedef struct IjkMediaPlayer IjkMediaPlayer; @property(nonatomic) IJKAVDiscard skipLoopFilter; @property(nonatomic) IJKAVDiscard skipFrame; -@property(nonatomic) int frameBufferCount; -@property(nonatomic) int maxFps; -@property(nonatomic) int frameDrop; -@property(nonatomic) BOOL pauseInBackground; +@property(nonatomic) int frameBufferCount; +@property(nonatomic) int maxFps; +@property(nonatomic) int frameDrop; +@property(nonatomic) BOOL pauseInBackground; +@property(nonatomic) BOOL videotoolboxEnabled; +@property(nonatomic) int frameMaxWidth; @property(nonatomic, strong) NSString* userAgent; diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.m b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.m index 3b030f8b..aea9838e 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/IJKFFOptions.m @@ -18,13 +18,16 @@ options.skipLoopFilter = IJK_AVDISCARD_ALL; options.skipFrame = IJK_AVDISCARD_NONREF; - options.frameBufferCount = 3; - options.maxFps = 30; - options.frameDrop = 0; - options.pauseInBackground = YES; + options.frameBufferCount = 3; + options.maxFps = 30; + options.frameDrop = 0; + options.pauseInBackground = YES; + + options.timeout = 30 * 1000 * 1000; // 30 seconds + options.userAgent = @""; + options.videotoolboxEnabled = YES; + options.frameMaxWidth = 960; - options.timeout = 30 * 1000 * 1000; // 30 seconds - options.userAgent = @""; return options; } @@ -43,6 +46,9 @@ ijkmp_set_picture_queue_capicity(mediaPlayer, _frameBufferCount); ijkmp_set_max_fps(mediaPlayer, _maxFps); ijkmp_set_framedrop(mediaPlayer, _frameDrop); + ijkmp_ios_set_videotoolbox_enabled(mediaPlayer, _videotoolboxEnabled); + ijkmp_ios_set_frame_max_width(mediaPlayer, _frameMaxWidth); + if (self.timeout > 0) { [self setFormatOption:@"timeout" diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/h264_sps_parser.h b/ios/IJKMediaPlayer/IJKMediaPlayer/h264_sps_parser.h new file mode 100644 index 00000000..78efb2ae --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/h264_sps_parser.h @@ -0,0 +1,260 @@ +/* + * h264_sps_parser.h + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef IJKMediaPlayer_h264_sps_parser_h +#define IJKMediaPlayer_h264_sps_parser_h + +#define AV_RB16(x) \ +((((const uint8_t*)(x))[0] << 8) | \ +((const uint8_t*)(x)) [1]) + +#define AV_RB24(x) \ +((((const uint8_t*)(x))[0] << 16) | \ +(((const uint8_t*)(x))[1] << 8) | \ +((const uint8_t*)(x))[2]) + +#define AV_RB32(x) \ +((((const uint8_t*)(x))[0] << 24) | \ +(((const uint8_t*)(x))[1] << 16) | \ +(((const uint8_t*)(x))[2] << 8) | \ +((const uint8_t*)(x))[3]) + +typedef struct +{ + const uint8_t *data; + const uint8_t *end; + int head; + uint64_t cache; +} nal_bitstream; + +static void +nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size) +{ + bs->data = data; + bs->end = data + size; + bs->head = 0; + // fill with something other than 0 to detect + // emulation prevention bytes + bs->cache = 0xffffffff; +} + +static uint64_t +nal_bs_read(nal_bitstream *bs, int n) +{ + uint64_t res = 0; + int shift; + + if (n == 0) + return res; + + // fill up the cache if we need to + while (bs->head < n) { + uint8_t a_byte; + bool check_three_byte; + + check_three_byte = true; + next_byte: + if (bs->data >= bs->end) { + // we're at the end, can't produce more than head number of bits + n = bs->head; + break; + } + // get the byte, this can be an emulation_prevention_three_byte that we need + // to ignore. + a_byte = *bs->data++; + if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0)) { + // next byte goes unconditionally to the cache, even if it's 0x03 + check_three_byte = false; + goto next_byte; + } + // shift bytes in cache, moving the head bits of the cache left + bs->cache = (bs->cache << 8) | a_byte; + bs->head += 8; + } + + // bring the required bits down and truncate + if ((shift = bs->head - n) > 0) + res = bs->cache >> shift; + else + res = bs->cache; + + // mask out required bits + if (n < 32) + res &= (1 << n) - 1; + + bs->head = shift; + + return res; +} + +static bool +nal_bs_eos(nal_bitstream *bs) +{ + return (bs->data >= bs->end) && (bs->head == 0); +} + +// read unsigned Exp-Golomb code +static int64_t +nal_bs_read_ue(nal_bitstream *bs) +{ + int i = 0; + + while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 32) + i++; + + return ((1 << i) - 1 + nal_bs_read(bs, i)); +} + +typedef struct +{ + uint64_t profile_idc; + uint64_t level_idc; + uint64_t sps_id; + + uint64_t chroma_format_idc; + uint64_t separate_colour_plane_flag; + uint64_t bit_depth_luma_minus8; + uint64_t bit_depth_chroma_minus8; + uint64_t qpprime_y_zero_transform_bypass_flag; + uint64_t seq_scaling_matrix_present_flag; + + uint64_t log2_max_frame_num_minus4; + uint64_t pic_order_cnt_type; + uint64_t log2_max_pic_order_cnt_lsb_minus4; + + uint64_t max_num_ref_frames; + uint64_t gaps_in_frame_num_value_allowed_flag; + uint64_t pic_width_in_mbs_minus1; + uint64_t pic_height_in_map_units_minus1; + + uint64_t frame_mbs_only_flag; + uint64_t mb_adaptive_frame_field_flag; + + uint64_t direct_8x8_inference_flag; + + uint64_t frame_cropping_flag; + uint64_t frame_crop_left_offset; + uint64_t frame_crop_right_offset; + uint64_t frame_crop_top_offset; + uint64_t frame_crop_bottom_offset; +} sps_info_struct; + +static void parseh264_sps(uint8_t *sps, uint32_t sps_size, int *level, int *profile, bool *interlaced, int32_t *max_ref_frames) +{ + nal_bitstream bs; + sps_info_struct sps_info = {0}; + + nal_bs_init(&bs, sps, sps_size); + + sps_info.profile_idc = nal_bs_read(&bs, 8); + nal_bs_read(&bs, 1); // constraint_set0_flag + nal_bs_read(&bs, 1); // constraint_set1_flag + nal_bs_read(&bs, 1); // constraint_set2_flag + nal_bs_read(&bs, 1); // constraint_set3_flag + nal_bs_read(&bs, 4); // reserved + sps_info.level_idc = nal_bs_read(&bs, 8); + sps_info.sps_id = nal_bs_read_ue(&bs); + + if (sps_info.profile_idc == 100 || + sps_info.profile_idc == 110 || + sps_info.profile_idc == 122 || + sps_info.profile_idc == 244 || + sps_info.profile_idc == 44 || + sps_info.profile_idc == 83 || + sps_info.profile_idc == 86) + { + sps_info.chroma_format_idc = nal_bs_read_ue(&bs); + if (sps_info.chroma_format_idc == 3) + sps_info.separate_colour_plane_flag = nal_bs_read(&bs, 1); + sps_info.bit_depth_luma_minus8 = nal_bs_read_ue(&bs); + sps_info.bit_depth_chroma_minus8 = nal_bs_read_ue(&bs); + sps_info.qpprime_y_zero_transform_bypass_flag = nal_bs_read(&bs, 1); + + sps_info.seq_scaling_matrix_present_flag = nal_bs_read (&bs, 1); + if (sps_info.seq_scaling_matrix_present_flag) + { + /* TODO: unfinished */ + } + } + sps_info.log2_max_frame_num_minus4 = nal_bs_read_ue(&bs); + if (sps_info.log2_max_frame_num_minus4 > 12) { + // must be between 0 and 12 + // don't early return here - the bits we are using (profile/level/interlaced/ref frames) + // might still be valid - let the parser go on and pray. + //return; + } + + sps_info.pic_order_cnt_type = nal_bs_read_ue(&bs); + if (sps_info.pic_order_cnt_type == 0) { + sps_info.log2_max_pic_order_cnt_lsb_minus4 = nal_bs_read_ue(&bs); + } + else if (sps_info.pic_order_cnt_type == 1) { // TODO: unfinished + /* + delta_pic_order_always_zero_flag = gst_nal_bs_read (bs, 1); + offset_for_non_ref_pic = gst_nal_bs_read_se (bs); + offset_for_top_to_bottom_field = gst_nal_bs_read_se (bs); + + num_ref_frames_in_pic_order_cnt_cycle = gst_nal_bs_read_ue (bs); + for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) + offset_for_ref_frame[i] = gst_nal_bs_read_se (bs); + */ + } + + sps_info.max_num_ref_frames = nal_bs_read_ue(&bs); + sps_info.gaps_in_frame_num_value_allowed_flag = nal_bs_read(&bs, 1); + sps_info.pic_width_in_mbs_minus1 = nal_bs_read_ue(&bs); + sps_info.pic_height_in_map_units_minus1 = nal_bs_read_ue(&bs); + + sps_info.frame_mbs_only_flag = nal_bs_read(&bs, 1); + if (!sps_info.frame_mbs_only_flag) + sps_info.mb_adaptive_frame_field_flag = nal_bs_read(&bs, 1); + + sps_info.direct_8x8_inference_flag = nal_bs_read(&bs, 1); + + sps_info.frame_cropping_flag = nal_bs_read(&bs, 1); + if (sps_info.frame_cropping_flag) { + sps_info.frame_crop_left_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_right_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_top_offset = nal_bs_read_ue(&bs); + sps_info.frame_crop_bottom_offset = nal_bs_read_ue(&bs); + } + + *level = (int)sps_info.level_idc; + *profile = (int)sps_info.profile_idc; + *interlaced = (int)!sps_info.frame_mbs_only_flag; + *max_ref_frames = (int)sps_info.max_num_ref_frames; +} + +bool validate_avcC_spc(uint8_t *extradata, uint32_t extrasize, int32_t *max_ref_frames, int *level, int *profile) +{ + // check the avcC atom's sps for number of reference frames and + // bail if interlaced, VDA does not handle interlaced h264. + bool interlaced = true; + uint8_t *spc = extradata + 6; + uint32_t sps_size = AV_RB16(spc); + if (sps_size) + parseh264_sps(spc+3, sps_size-1, level, profile, &interlaced, max_ref_frames); + if (interlaced) + return false; + return true; +} + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.h index 3d6dfd98..cacea8df 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.h +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.h @@ -27,3 +27,5 @@ IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*)); void ijkmp_ios_set_glview(IjkMediaPlayer *mp, IJKSDLGLView *glView); +void ijkmp_ios_set_videotoolbox_enabled(IjkMediaPlayer *mp, BOOL enabled); +void ijkmp_ios_set_frame_max_width(IjkMediaPlayer *mp, int width); diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.m index 23b44c87..66fd9a58 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/ijkplayer_ios.m @@ -30,6 +30,7 @@ #include "ijkplayer/ff_ffplay.h" #include "ijkplayer/ijkplayer_internal.h" #include "ijkplayer/pipeline/ffpipeline_ffplay.h" +#include "ffpipeline_ios.h" IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*)) { @@ -45,7 +46,7 @@ IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*)) if (!mp->ffplayer->vout) goto fail; - mp->ffplayer->pipeline = ffpipeline_create_from_ffplay(mp->ffplayer); + mp->ffplayer->pipeline = ffpipeline_create_from_ios(mp->ffplayer); if (!mp->ffplayer->pipeline) goto fail; @@ -74,3 +75,43 @@ void ijkmp_ios_set_glview(IjkMediaPlayer *mp, IJKSDLGLView *glView) pthread_mutex_unlock(&mp->mutex); MPTRACE("ijkmp_ios_set_view(glView=%p)=void\n", (void*)glView); } + +void ijkmp_ios_set_frame_max_width_l(IjkMediaPlayer *mp, int width) +{ + assert(mp); + assert(mp->ffplayer); + assert(mp->ffplayer->pipeline); + ffpipeline_ios_set_frame_max_width(mp->ffplayer->pipeline, width); +} + +void ijkmp_ios_set_frame_max_width(IjkMediaPlayer *mp, int width) +{ + assert(mp); + MPTRACE("%s (width=%d)\n", __func__, width); + pthread_mutex_lock(&mp->mutex); + ijkmp_ios_set_frame_max_width_l(mp, width); + pthread_mutex_unlock(&mp->mutex); + MPTRACE("%s after(width=%d)\n", __func__, width); +} + +void ijkmp_ios_set_videotoolbox_enabled_l(IjkMediaPlayer *mp, BOOL enabled) +{ + assert(mp); + assert(mp->ffplayer); + assert(mp->ffplayer->pipeline); + if (enabled == YES) { + ffpipeline_ios_set_videotoolbox_enabled(mp->ffplayer->pipeline, 1); + } else { + ffpipeline_ios_set_videotoolbox_enabled(mp->ffplayer->pipeline, 0); + } +} + +void ijkmp_ios_set_videotoolbox_enabled(IjkMediaPlayer *mp, BOOL enabled) +{ + assert(mp); + MPTRACE("%s enable(EnableFlag=%d)\n", __func__, enabled); + pthread_mutex_lock(&mp->mutex); + ijkmp_ios_set_videotoolbox_enabled_l(mp, enabled); + pthread_mutex_unlock(&mp->mutex); + MPTRACE("%s enable(EnableFlag=%d)\n", __func__, enabled); +} diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.h new file mode 100644 index 00000000..57ec4d10 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * IJKVideoToolBox.h + ***************************************************************************** + * + * copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IJKMediaPlayer_videotoolbox_core_h +#define IJKMediaPlayer_videotoolbox_core_h + + +#import +#include "ff_ffinc.h" +#include "ff_fferror.h" +#include "ff_ffmsg.h" +#include "ff_ffplay.h" + + + +typedef struct VTBPicture { + double pts; + double dts; + double sort; + CVPixelBufferRef cvBufferRef; + uint64_t width; + uint64_t height; +} VTBPicture; + + +typedef struct sort_queue { + double dts; + double pts; + double sort; + int64_t width; + int64_t height; + CVPixelBufferRef pixel_buffer_ref; + volatile struct sort_queue *nextframe; +} sort_queue; + + +typedef struct VideoToolBoxContext { + FFPlayer *ffp; + AVCodecContext video_dec_ctx; + int width; + int height; + volatile bool refresh_request; + VTDecompressionSessionRef m_vt_session; + CMFormatDescriptionRef m_fmt_desc; + const char *m_pformat_name; + VTBPicture m_videobuffer; + double m_sort_time_offset; + pthread_mutex_t m_queue_mutex; + volatile sort_queue *m_sort_queue; + volatile int32_t m_queue_depth; + int32_t m_max_ref_frames; + bool m_convert_bytestream; + bool m_convert_3byteTo4byteNALSize; +} VideoToolBoxContext ; + + +VideoToolBoxContext* init_videotoolbox(FFPlayer* ffp, AVCodecContext* ic); + +int videotoolbox_decode_video(VideoToolBoxContext* context, AVCodecContext *avctx, const AVPacket *avpkt,int* got_picture_ptr); + +void dealloc_videotoolbox(VideoToolBoxContext* context); + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.m new file mode 100644 index 00000000..8bdc796e --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/IJKVideoToolBox.m @@ -0,0 +1,774 @@ +/***************************************************************************** + * IJKVideoToolBox.m + ***************************************************************************** + * + * copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "IJKVideoToolBox.h" +#include "ijksdl_vout_overlay_videotoolbox.h" +#include "ffpipeline_ios.h" +#import +#import +#import +#include +#include "libavformat/avc.h" +#include "ijksdl_vout_ios_gles2.h" +#include "h264_sps_parser.h" + +#define IJK_VTB_FCC_AVC SDL_FOURCC('C', 'c', 'v', 'a') +#define IJK_VTB_FCC_ESD SDL_FOURCC('s', 'd', 's', 'e') +#define kVTFormatH264 SDL_FOURCC('1', 'c', 'v', 'a') +#define NOPTS_VAL (-1LL<<52) + + +static double GetSystemTime() +{ + return ((int64_t)CVGetCurrentHostTime() * 1000.0) / ((int64_t)CVGetHostClockFrequency()); +} + +static void SortQueuePop(VideoToolBoxContext* context) +{ + if (!context->m_sort_queue || context->m_queue_depth == 0) { + return; + } + pthread_mutex_lock(&context->m_queue_mutex); + volatile sort_queue *top_frame = context->m_sort_queue; + context->m_sort_queue = context->m_sort_queue->nextframe; + context->m_queue_depth--; + pthread_mutex_unlock(&context->m_queue_mutex); + CVBufferRelease(top_frame->pixel_buffer_ref); + free((void*)top_frame); +} + +static void CFDictionarySetSInt32(CFMutableDictionaryRef dictionary, CFStringRef key, SInt32 numberSInt32) +{ + CFNumberRef number; + number = CFNumberCreate(NULL, kCFNumberSInt32Type, &numberSInt32); + CFDictionarySetValue(dictionary, key, number); + CFRelease(number); +} + + +static CMSampleBufferRef CreateSampleBufferFrom(CMFormatDescriptionRef fmt_desc, void *demux_buff, size_t demux_size) +{ + OSStatus status; + CMBlockBufferRef newBBufOut = NULL; + CMSampleBufferRef sBufOut = NULL; + + status = CMBlockBufferCreateWithMemoryBlock( + NULL, + demux_buff, + demux_size, + kCFAllocatorNull, + NULL, + 0, + demux_size, + FALSE, + &newBBufOut); + + if (!status) { + status = CMSampleBufferCreate( + NULL, + newBBufOut, + TRUE, + 0, + 0, + fmt_desc, + 1, + 0, + NULL, + 0, + NULL, + &sBufOut); + } + + CFRelease(newBBufOut); + if (status == 0) { + return sBufOut; + } else { + return NULL; + } +} + + + + +static bool GetVTBPicture(VideoToolBoxContext* context, VTBPicture* pVTBPicture) +{ + *pVTBPicture = context->m_videobuffer; + + if (context->m_sort_queue == NULL) { + return false; + } + pthread_mutex_lock(&context->m_queue_mutex); + + pVTBPicture->dts = context->m_sort_queue->dts; + pVTBPicture->pts = context->m_sort_queue->pts; + pVTBPicture->width = context->m_sort_queue->width; + pVTBPicture->height = context->m_sort_queue->height; + pVTBPicture->cvBufferRef = CVBufferRetain(context->m_sort_queue->pixel_buffer_ref); + + pthread_mutex_unlock(&context->m_queue_mutex); + + return true; +} + +static void vtb_free_picture(Frame *vp) +{ + if (vp->bmp) { + SDL_VoutFreeYUVOverlay(vp->bmp); + vp->bmp = NULL; + } +} + +static void vtb_alloc_picture(FFPlayer *ffp) +{ + VideoState *is = ffp->is; + Frame *vp = &is->pictq.queue[is->pictq.windex]; + vtb_free_picture(vp); + vp->bmp = SDL_Vout_CreateOverlay(vp->width, vp->height, SDL_FCC_NV12, ffp->vout); + if (!vp->bmp) { + av_log(NULL, AV_LOG_FATAL, + "Error: can't alloc nv12 overlay \n"); + vtb_free_picture(vp); + } + SDL_LockMutex(is->pictq.mutex); + vp->allocated = 1; + SDL_CondSignal(is->pictq.cond); + SDL_UnlockMutex(is->pictq.mutex); +} + +static int vtb_queue_picture( + FFPlayer* ffp, + VTBPicture* picture, + double pts, + double duration, + int64_t pos, + int serial) +{ + VideoState *is = ffp->is; + Frame *vp; + + if (!(vp = ffp_frame_queue_peek_writable(&is->pictq))) + return -1; + + vp->sar.num = 1; + vp->sar.den = 1; + + /* alloc or resize hardware picture buffer */ + if (!vp->bmp || vp->reallocate || !vp->allocated || + vp->width != picture->width || + vp->height != picture->height) { + + if (vp->width != picture->width || vp->height != picture->height) + ffp_notify_msg3(ffp, FFP_MSG_VIDEO_SIZE_CHANGED, (int)picture->width, (int)picture->height); + + vp->allocated = 0; + vp->reallocate = 0; + vp->width = (int)picture->width; + vp->height = (int)picture->height; + + /* the allocation must be done in the main thread to avoid + locking problems. */ + vtb_alloc_picture(ffp); + + if (is->videoq.abort_request) + return -1; + } + /* if the frame is not skipped, then display it */ + if (vp->bmp) { + /* get a pointer on the bitmap */ + SDL_VoutLockYUVOverlay(vp->bmp); + SDL_VoutOverlayVideoToolBox_FillFrame(vp->bmp,picture); + /* update the bitmap content */ + SDL_VoutUnlockYUVOverlay(vp->bmp); + + CVBufferRelease(picture->cvBufferRef); + + vp->pts = pts; + vp->duration = duration; + vp->pos = pos; + vp->serial = serial; + ffp_frame_queue_push(&is->pictq); + } + return 0; +} + + + +static CFDictionaryRef CreateDictionaryWithPkt(double time, double dts, double pts) +{ + CFStringRef key[3] = { + CFSTR("PKT_SORT"), + CFSTR("PKT_DTS"), + CFSTR("PKT_PTS") + }; + CFNumberRef value[3]; + CFDictionaryRef display_time; + + value[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &time); + value[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &dts); + value[2] = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &pts); + + display_time = CFDictionaryCreate( + kCFAllocatorDefault, (const void **)&key, (const void **)&value, 3, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFRelease(value[0]); + CFRelease(value[1]); + CFRelease(value[2]); + + return display_time; +} + + + +static void GetPktTSFromRef(CFDictionaryRef inFrameInfoDictionary, sort_queue *frame) +{ + frame->sort = -1.0; + frame->dts = NOPTS_VAL; + frame->pts = NOPTS_VAL; + if (inFrameInfoDictionary == NULL) + return; + + CFNumberRef value[3]; + + value[0] = (CFNumberRef)CFDictionaryGetValue(inFrameInfoDictionary, CFSTR("PKT_SORT")); + if (value[0]) + CFNumberGetValue(value[0], kCFNumberDoubleType, &frame->sort); + value[1] = (CFNumberRef)CFDictionaryGetValue(inFrameInfoDictionary, CFSTR("PKT_DTS")); + if (value[1]) + CFNumberGetValue(value[1], kCFNumberDoubleType, &frame->dts); + value[2] = (CFNumberRef)CFDictionaryGetValue(inFrameInfoDictionary, CFSTR("PKT_PTS")); + if (value[2]) + CFNumberGetValue(value[2], kCFNumberDoubleType, &frame->pts); + + return; +} + +void VTDecoderCallback(void *decompressionOutputRefCon, + void *sourceFrameRefCon, + OSStatus status, + VTDecodeInfoFlags infoFlags, + CVImageBufferRef imageBuffer, + CMTime presentationTimeStamp, + CMTime presentationDuration) +{ + @autoreleasepool { + VideoToolBoxContext *ctx = (VideoToolBoxContext*)decompressionOutputRefCon; + + if (status != 0) { + ALOGI("decode callback %d \n", (int)status); + return; + } + if (imageBuffer == NULL) { + ALOGI("imageBuffer null\n"); + return; + } + OSType format_type = CVPixelBufferGetPixelFormatType(imageBuffer); + if (format_type != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) { + ALOGI("format_type error \n"); + return; + } + if (kVTDecodeInfo_FrameDropped & infoFlags) { + ALOGI("droped\n"); + return; + } + + if (ctx->refresh_request) { + while (ctx->m_queue_depth > 0) { + SortQueuePop(ctx); + } + ctx->refresh_request = false; + } + + sort_queue *newFrame = (sort_queue*)malloc(sizeof(sort_queue)); + newFrame->nextframe = NULL; + + if (CVPixelBufferIsPlanar(imageBuffer)) { + newFrame->width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0); + newFrame->height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0); + } else { + newFrame->width = CVPixelBufferGetWidth(imageBuffer); + newFrame->height = CVPixelBufferGetHeight(imageBuffer); + } + + + newFrame->pixel_buffer_ref = CVBufferRetain(imageBuffer); + GetPktTSFromRef(sourceFrameRefCon, newFrame); + CFRelease(sourceFrameRefCon); + + if ((newFrame->pts != NOPTS_VAL) || (newFrame->dts != NOPTS_VAL)) { + if (newFrame->pts == NOPTS_VAL) + newFrame->sort = newFrame->dts; + else + newFrame->sort = newFrame->pts; + } + pthread_mutex_lock(&ctx->m_queue_mutex); + volatile sort_queue *queueWalker = ctx->m_sort_queue; + if (!queueWalker || (newFrame->sort < queueWalker->sort)) { + newFrame->nextframe = queueWalker; + ctx->m_sort_queue = newFrame; + } else { + bool frameInserted = false; + volatile sort_queue *nextFrame = NULL; + while (!frameInserted) { + nextFrame = queueWalker->nextframe; + if (!nextFrame || (newFrame->sort < nextFrame->sort)) { + newFrame->nextframe = nextFrame; + queueWalker->nextframe = newFrame; + frameInserted = true; + } + queueWalker = nextFrame; + } + } + ctx->m_queue_depth++; + pthread_mutex_unlock(&ctx->m_queue_mutex); + // ALOGI("%lf %lf %lf \n", newFrame->sort,newFrame->pts, newFrame->dts); + + // ALOGI("display queue deep %d\n", ctx->m_queue_depth); + + + VTBPicture picture; + if (ctx->ffp->is == NULL || ctx->ffp->is->abort_request || ctx->ffp->is->viddec.queue->abort_request) { + while (ctx->m_queue_depth > 0) { + SortQueuePop(ctx); + } + return; + } + //ALOGI("depth %d %d\n", ctx->m_queue_depth, ctx->m_max_ref_frames); + if ((ctx->m_queue_depth > ctx->m_max_ref_frames)) { + if (true == GetVTBPicture(ctx, &picture)) { + double pts; + double duration; + int64_t vtb_pts_us = (int64_t)picture.pts; + int64_t videotoolbox_pts = vtb_pts_us; + + AVRational tb = ctx->ffp->is->video_st->time_base; + AVRational frame_rate = av_guess_frame_rate(ctx->ffp->is->ic, ctx->ffp->is->video_st, NULL); + duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational) {frame_rate.den, frame_rate.num}) : 0); + pts = (videotoolbox_pts == AV_NOPTS_VALUE) ? NAN : videotoolbox_pts * av_q2d(tb); + + vtb_queue_picture(ctx->ffp, &picture, pts, duration, 0, ctx->ffp->is->viddec.pkt_serial); + SortQueuePop(ctx); + } else { + ALOGI("Get Picture failure!!!\n"); + } + + } + + + } +} + + + +void CreateVTBSession(VideoToolBoxContext* context, int width, int height, CMFormatDescriptionRef fmt_desc) +{ + VTDecompressionSessionRef vt_session = NULL; + CFMutableDictionaryRef destinationPixelBufferAttributes; + VTDecompressionOutputCallbackRecord outputCallback; + OSStatus status; + int width_frame_max = ffpipeline_ios_get_frame_max_width(context->ffp->pipeline); + + if (width > width_frame_max) { + double w_scaler = (float)width_frame_max / width; + width = width_frame_max; + height = height * w_scaler; + } + + ALOGI("after scale width %d height %d \n", width, height); + + destinationPixelBufferAttributes = CFDictionaryCreateMutable( + NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionarySetSInt32(destinationPixelBufferAttributes, + kCVPixelBufferPixelFormatTypeKey, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); + CFDictionarySetSInt32(destinationPixelBufferAttributes, + kCVPixelBufferWidthKey, width); + CFDictionarySetSInt32(destinationPixelBufferAttributes, + kCVPixelBufferHeightKey, height); + outputCallback.decompressionOutputCallback = VTDecoderCallback; + outputCallback.decompressionOutputRefCon = context ; + status = VTDecompressionSessionCreate( + kCFAllocatorDefault, + fmt_desc, + NULL, + destinationPixelBufferAttributes, + &outputCallback, + &vt_session); + + + if (status != noErr) { + context->m_vt_session = NULL; + NSError* error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSLog(@"Error %@", [error description]); + ALOGI("%s - failed with status = (%d)", __FUNCTION__, (int)status); + } else { + context->m_vt_session =(void*) vt_session; + } + CFRelease(destinationPixelBufferAttributes); +} + +int videotoolbox_decode_video(VideoToolBoxContext* context, AVCodecContext *avctx, const AVPacket *avpkt, int* got_picture_ptr) +{ + if (!avpkt || !avpkt->data) { + return -1; + } + OSStatus status = 0; + double sort_time = GetSystemTime(); + uint32_t decoderFlags = 0; + CFDictionaryRef frame_info = NULL;; + CMSampleBufferRef sample_buff = NULL; + AVIOContext *pb = NULL; + int demux_size = 0; + uint8_t *demux_buff = NULL; + uint8_t *pData = avpkt->data; + int iSize = avpkt->size; + double pts = avpkt->pts; + double dts = avpkt->dts; + + if (!context) { + goto failed; + } + + frame_info = CreateDictionaryWithPkt(sort_time - context->m_sort_time_offset, dts, pts); + + if (context->m_convert_bytestream) { + ALOGI("the buffer should m_convert_byte\n"); + if(avio_open_dyn_buf(&pb) < 0) { + goto failed; + } + ff_avc_parse_nal_units(pb, pData, iSize); + demux_size = avio_close_dyn_buf(pb, &demux_buff); + ALOGI("demux_size:%d\n", demux_size); + if (demux_size == 0) { + goto failed; + } + sample_buff = CreateSampleBufferFrom(context->m_fmt_desc, demux_buff, demux_size); + } else if (context->m_convert_3byteTo4byteNALSize) { + ALOGI("3byteto4byte\n"); + if (avio_open_dyn_buf(&pb) < 0) { + goto failed; + } + + uint32_t nal_size; + uint8_t *end = avpkt->data + avpkt->size; + uint8_t *nal_start = pData; + while (nal_start < end) { + nal_size = AV_RB24(nal_start); + avio_wb32(pb, nal_size); + nal_start += 3; + avio_write(pb, nal_start, nal_size); + nal_start += nal_size; + } + demux_size = avio_close_dyn_buf(pb, &demux_buff); + sample_buff = CreateSampleBufferFrom(context->m_fmt_desc, demux_buff, demux_size); + } else { + sample_buff = CreateSampleBufferFrom(context->m_fmt_desc, pData, iSize); + } + if (!sample_buff) { + if (demux_size) { + av_free(demux_buff); + } + ALOGI("%s - CreateSampleBufferFrom failed", __FUNCTION__); + goto failed; + } + + if (pts == 0) { + pts = dts; + } + + decoderFlags = 0; + + if (dts < 0 || pts < 0) { + goto failed; + } + //ALOGI("Decode before \n!!!!!!!"); + status = VTDecompressionSessionDecodeFrame(context->m_vt_session, sample_buff, decoderFlags, (void*)frame_info, 0); + //ALOGI("Decode after \n!!!!!!!"); + if (status != 0) { + if (status == kVTInvalidSessionErr) { + if(context->m_vt_session) { + VTDecompressionSessionInvalidate(context->m_vt_session); + CFRelease(context->m_vt_session); + } + while (context->m_queue_depth > 0) { + SortQueuePop(context); + } + CreateVTBSession(context, context->ffp->is->viddec.avctx->width, context->ffp->is->viddec.avctx->height, context->m_fmt_desc); + status = VTDecompressionSessionDecodeFrame(context->m_vt_session, sample_buff, decoderFlags, (void*)frame_info, 0); + } + if (status != 0) { + goto failed; + } + } + + status = VTDecompressionSessionWaitForAsynchronousFrames(context->m_vt_session); + + if (status != 0) { + goto failed; + } + + if (sample_buff) { + CFRelease(sample_buff); + } + if (demux_size) { + av_free(demux_buff); + } + + *got_picture_ptr = 1; + return 0; +failed: + if (sample_buff) { + CFRelease(sample_buff); + } + if (frame_info) { + CFRelease(frame_info); + } + if (demux_size) { + av_free(demux_buff); + } + *got_picture_ptr = 0; + return -1; + +} + +static void dict_set_string(CFMutableDictionaryRef dict, CFStringRef key, const char * value) +{ + CFStringRef string; + string = CFStringCreateWithCString(NULL, value, kCFStringEncodingASCII); + CFDictionarySetValue(dict, key, string); + CFRelease(string); +} + +static void dict_set_boolean(CFMutableDictionaryRef dict, CFStringRef key, BOOL value) +{ + CFDictionarySetValue(dict, key, value ? kCFBooleanTrue: kCFBooleanFalse); +} + + +static void dict_set_object(CFMutableDictionaryRef dict, CFStringRef key, CFTypeRef *value) +{ + CFDictionarySetValue(dict, key, value); + CFRelease(value); +} + +static void dict_set_data(CFMutableDictionaryRef dict, CFStringRef key, uint8_t * value, uint64_t length) +{ + CFDataRef data; + data = CFDataCreate(NULL, value, length); + CFDictionarySetValue(dict, key, data); + CFRelease(data); +} + +static void dict_set_i32(CFMutableDictionaryRef dict, CFStringRef key, + int32_t value) +{ + CFNumberRef number; + number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); + CFDictionarySetValue(dict, key, number); + CFRelease(number); +} + +static CMFormatDescriptionRef CreateFormatDescriptionFromCodecData(Uint32 format_id, int width, int height, const uint8_t *extradata, int extradata_size, uint32_t atom) +{ + CMFormatDescriptionRef fmt_desc = NULL; + OSStatus status; + + CFMutableDictionaryRef par = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef atoms = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef extensions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + /* CVPixelAspectRatio dict */ + dict_set_i32(par, CFSTR ("HorizontalSpacing"), 0); + dict_set_i32(par, CFSTR ("VerticalSpacing"), 0); + /* SampleDescriptionExtensionAtoms dict */ + dict_set_data(atoms, CFSTR ("avcC"), (uint8_t *)extradata, extradata_size); + + /* Extensions dict */ + dict_set_string(extensions, CFSTR ("CVImageBufferChromaLocationBottomField"), "left"); + dict_set_string(extensions, CFSTR ("CVImageBufferChromaLocationTopField"), "left"); + dict_set_boolean(extensions, CFSTR("FullRangeVideo"), FALSE); + dict_set_object(extensions, CFSTR ("CVPixelAspectRatio"), (CFTypeRef *) par); + dict_set_object(extensions, CFSTR ("SampleDescriptionExtensionAtoms"), (CFTypeRef *) atoms); + status = CMVideoFormatDescriptionCreate(NULL, format_id, width, height, extensions, &fmt_desc); + + if (status == 0) + return fmt_desc; + else + return NULL; +} + +void dealloc_videotoolbox(VideoToolBoxContext* context) +{ + while (context && context->m_queue_depth > 0) { + SortQueuePop(context); + } + + if (context && context->m_vt_session) { + VTDecompressionSessionInvalidate(context->m_vt_session); + CFRelease(context->m_vt_session); + } +} + + + +VideoToolBoxContext* init_videotoolbox(FFPlayer* ffp, AVCodecContext* ic) +{ + int width = ic->width; + int height = ic->height; + int level = ic->level; + int profile = ic->profile; + int sps_level = 0; + int sps_profile = 0; + int extrasize = ic->extradata_size; + int codec = ic->codec_id; + uint8_t* extradata = ic->extradata; + + VideoToolBoxContext* context_vtb = malloc(sizeof(VideoToolBoxContext)); + if (!context_vtb) { + goto failed; + } + + context_vtb->m_convert_bytestream = false; + context_vtb->m_convert_3byteTo4byteNALSize = false; + context_vtb->refresh_request = false; + context_vtb->m_max_ref_frames = 0; + context_vtb->ffp = ffp; + + switch (profile) { + case FF_PROFILE_H264_HIGH_10: + case FF_PROFILE_H264_HIGH_10_INTRA: + case FF_PROFILE_H264_HIGH_422: + case FF_PROFILE_H264_HIGH_422_INTRA: + case FF_PROFILE_H264_HIGH_444_PREDICTIVE: + case FF_PROFILE_H264_HIGH_444_INTRA: + case FF_PROFILE_H264_CAVLC_444: + goto failed; + } + + if (width < 0 || height < 0) { + goto failed; + } + + switch (codec) { + case AV_CODEC_ID_H264: + if (extrasize < 7 || extradata == NULL) { + ALOGI("%s - avcC atom too data small or missing", __FUNCTION__); + goto failed; + } + + if (extradata[0] == 1) { + if (!validate_avcC_spc(extradata, extrasize, &context_vtb->m_max_ref_frames, &sps_level, &sps_profile)) { + goto failed; + } + if (level == 0 && sps_level > 0) + level = sps_level; + + if (profile == 0 && sps_profile > 0) + profile = sps_profile; + if (profile == FF_PROFILE_H264_MAIN && level == 32 && context_vtb->m_max_ref_frames > 4) { + ALOGE("%s - Main@L3.2 detected, VTB cannot decode with %d ref frames", __FUNCTION__, context_vtb->m_max_ref_frames); + goto failed; + } + + if (extradata[4] == 0xFE) { + extradata[4] = 0xFF; + context_vtb->m_convert_3byteTo4byteNALSize = true; + } + + + context_vtb->m_fmt_desc = CreateFormatDescriptionFromCodecData(kVTFormatH264, width, height, extradata, extrasize, IJK_VTB_FCC_AVC); + + ALOGI("%s - using avcC atom of size(%d), ref_frames(%d)", __FUNCTION__, extrasize, context_vtb->m_max_ref_frames); + } else { + if ((extradata[0] == 0 && extradata[1] == 0 && extradata[2] == 0 && extradata[3] == 1) || + (extradata[0] == 0 && extradata[1] == 0 && extradata[2] == 1)) { + AVIOContext *pb; + if (avio_open_dyn_buf(&pb) < 0) { + goto failed; + } + + context_vtb->m_convert_bytestream = true; + ff_isom_write_avcc(pb, extradata, extrasize); + extradata = NULL; + + extrasize = avio_close_dyn_buf(pb, &extradata); + + if (!validate_avcC_spc(extradata, extrasize, &context_vtb->m_max_ref_frames, &sps_level, &sps_profile)) { + av_free(extradata); + goto failed; + } + + context_vtb->m_fmt_desc = CreateFormatDescriptionFromCodecData(kVTFormatH264, width, height, extradata, extrasize, IJK_VTB_FCC_AVC); + + av_free(extradata); + } else { + ALOGI("%s - invalid avcC atom data", __FUNCTION__); + goto failed; + } + } + context_vtb->m_pformat_name = "vtb-h264"; + break; + default: + goto failed; + } + if (context_vtb->m_fmt_desc == NULL) { + context_vtb->m_pformat_name = ""; + goto failed; + } + + + CreateVTBSession(context_vtb, width, height, context_vtb->m_fmt_desc); + context_vtb->m_sort_queue = 0; + if (context_vtb->m_vt_session == NULL) { + if (context_vtb->m_fmt_desc) { + CFRelease(context_vtb->m_fmt_desc); + context_vtb->m_fmt_desc = NULL; + } + context_vtb->m_pformat_name = ""; + goto failed; + } + context_vtb->width = width; + context_vtb->height = height; + context_vtb->m_queue_depth = 0; + memset(&context_vtb->m_videobuffer, 0, sizeof(VTBPicture)); + if (context_vtb->m_max_ref_frames <= 0) { + context_vtb->m_max_ref_frames = 2; + } + if (context_vtb->m_max_ref_frames > 5) { + context_vtb->m_max_ref_frames = 5; + } + + ALOGI("m_max_ref_frames %d \n", context_vtb->m_max_ref_frames); + + context_vtb->m_sort_time_offset = GetSystemTime(); + + return context_vtb; + +failed: + if (context_vtb) { + free(context_vtb); + } + return NULL; +} diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.c b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.c new file mode 100644 index 00000000..cee1d40b --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.c @@ -0,0 +1,100 @@ +/* + * ffpipeline_ios.c + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ffpipeline_ios.h" +#include "ffpipenode_ios_videotoolbox_vdec.h" +#include "ffpipenode_ios_videotoolbox_vout.h" +#include "ffpipenode_ffplay_vdec.h" +#include "ff_ffplay.h" + +typedef struct IJKFF_Pipeline_Opaque { + FFPlayer *ffp; + int m_max_frame_width; + int m_videotoolbox_enable; +} IJKFF_Pipeline_Opaque; + +static void func_destroy(IJKFF_Pipeline *pipeline) +{ +} + +void ffpipeline_ios_set_frame_max_width(IJKFF_Pipeline *pipeline, int max_frame_width) +{ + IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; + opaque->m_max_frame_width = max_frame_width; +} + + +int ffpipeline_ios_get_frame_max_width(IJKFF_Pipeline *pipeline) +{ + IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; + return opaque->m_max_frame_width; +} + + +void ffpipeline_ios_set_videotoolbox_enabled(IJKFF_Pipeline *pipeline, int enabled) +{ + IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; + opaque->m_videotoolbox_enable = enabled; +} + +static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp) +{ + IJKFF_Pipenode* node = NULL; + IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; + if (opaque->m_videotoolbox_enable) { + node = ffpipenode_create_video_decoder_from_ios_videotoolbox(ffp); + } + if (node == NULL) { + ALOGE("vtb fail!!! switch to ffmpeg decode!!!! \n"); + node = ffpipenode_create_video_decoder_from_ffplay(ffp); + } + return node; +} + +static IJKFF_Pipenode *func_open_video_output(IJKFF_Pipeline *pipeline, FFPlayer *ffp) +{ + return ffpipenode_create_video_output_from_ios_videotoolbox(ffp); +} + +static SDL_Class g_pipeline_class = { + .name = "ffpipeline_ios", +}; + +IJKFF_Pipeline *ffpipeline_create_from_ios(FFPlayer *ffp) +{ + IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque)); + if (!pipeline) + return pipeline; + + IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; + opaque->ffp = ffp; + opaque->m_max_frame_width = 0; + opaque->m_videotoolbox_enable = 0; + pipeline->func_destroy = func_destroy; + pipeline->func_open_video_decoder = func_open_video_decoder; + pipeline->func_open_video_output = func_open_video_output; + + return pipeline; +fail: + ffpipeline_free_p(&pipeline); + return NULL; +} diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.h new file mode 100644 index 00000000..98e88c97 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipeline_ios.h @@ -0,0 +1,36 @@ +/* + * ffpipeline_ios.h + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FFPLAY__FF_FFPIPELINE_IOS_H +#define FFPLAY__FF_FFPIPELINE_IOS_H + +#include "ijkplayer/ff_ffpipeline.h" + +typedef struct FFPlayer FFPlayer; +typedef struct IJKFF_Pipeline IJKFF_Pipeline; + +IJKFF_Pipeline *ffpipeline_create_from_ios(FFPlayer *ffp); +void ffpipeline_ios_set_frame_max_width(IJKFF_Pipeline *pipeline, int width); +int ffpipeline_ios_get_frame_max_width(IJKFF_Pipeline *pipeline); +void ffpipeline_ios_set_videotoolbox_enabled(IJKFF_Pipeline *pipeline, int enabled); + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.h new file mode 100644 index 00000000..5e79bd7a --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.h @@ -0,0 +1,32 @@ +/* + * ffpipenode_ios_videotoolbox_vdec.h + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FFPLAY__FF_FFPIPENODE_IOS_VIDEOTOOLBOX_DEC_H +#define FFPLAY__FF_FFPIPENODE_IOS_VIDEOTOOLBOX_DEC_H + +#include "ijkplayer/ff_ffpipenode.h" + +typedef struct FFPlayer FFPlayer; + +IJKFF_Pipenode *ffpipenode_create_video_decoder_from_ios_videotoolbox(FFPlayer *ffp); + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.m new file mode 100644 index 00000000..c6f29c60 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vdec.m @@ -0,0 +1,185 @@ +/* + * ffpipenode_ios_videotoolbox_vdec.m + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ffpipenode_ios_videotoolbox_vdec.h" +#include "IJKVideoToolBox.h" +#include "ijksdl_vout_overlay_videotoolbox.h" +#include "ijkplayer/ff_ffpipeline.h" +#include "ijkplayer/ff_ffpipenode.h" +#include "ijkplayer/ff_ffplay.h" +#include "ijksdl_mutex.h" +#include "ijksdl_vout_ios_gles2.h" + +typedef struct IJKFF_Pipenode_Opaque { + IJKFF_Pipeline *pipeline; + FFPlayer *ffp; + Decoder *decoder; + VideoToolBoxContext *context; + AVCodecContext *avctx; // not own + SDL_Thread* video_fill_thread; + SDL_Thread _video_fill_thread; +} IJKFF_Pipenode_Opaque; + + + +int decoder_decode_frame_videotoolbox(VideoToolBoxContext* context) { + + FFPlayer *ffp = context->ffp; + VideoState *is = ffp->is; + Decoder *d = &is->viddec; + int got_frame = 0; + do { + int ret = -1; + if (is->abort_request || d->queue->abort_request) { + return -1; + } + + if (!d->packet_pending || d->queue->serial != d->pkt_serial) { + AVPacket pkt; + do { + if (d->queue->nb_packets == 0) + SDL_CondSignal(d->empty_queue_cond); + if (ffp_packet_queue_get_or_buffering(ffp, d->queue, &pkt, &d->pkt_serial, &d->finished) < 0) + return -1; + if (ffp_is_flush_packet(&pkt)) { + avcodec_flush_buffers(d->avctx); + context->refresh_request = true; + + d->finished = 0; + d->next_pts = d->start_pts; + d->next_pts_tb = d->start_pts_tb; + } + } while (ffp_is_flush_packet(&pkt) || d->queue->serial != d->pkt_serial); + av_free_packet(&d->pkt); + d->pkt_temp = d->pkt = pkt; + d->packet_pending = 1; + } + + ret = videotoolbox_decode_video(context, d->avctx, &d->pkt_temp, &got_frame); + if (ret < 0) { + d->packet_pending = 0; + } else { + d->pkt_temp.dts = + d->pkt_temp.pts = AV_NOPTS_VALUE; + if (d->pkt_temp.data) { + if (d->avctx->codec_type != AVMEDIA_TYPE_AUDIO) + ret = d->pkt_temp.size; + d->pkt_temp.data += ret; + d->pkt_temp.size -= ret; + if (d->pkt_temp.size <= 0) + d->packet_pending = 0; + } else { + if (!got_frame) { + d->packet_pending = 0; + d->finished = d->pkt_serial; + } + } + } + } while (!got_frame && !d->finished); + return got_frame; +} + +int videotoolbox_video_thread(void *arg) +{ + IJKFF_Pipenode_Opaque* opaque = (IJKFF_Pipenode_Opaque*) arg; + FFPlayer *ffp = opaque->ffp; + VideoState *is = ffp->is; + Decoder *d = &is->viddec; + int ret = 0; + opaque->context->ffp = ffp; + + for (;;) { + + if (is->abort_request || d->queue->abort_request) { + return -1; + } + @autoreleasepool { + ret = decoder_decode_frame_videotoolbox(opaque->context); + } + if (ret < 0) + goto the_end; + if (!ret) + continue; + + if (ret < 0) + goto the_end; + } +the_end: + return 0; +} + + + +static void func_destroy(IJKFF_Pipenode *node) +{ + ALOGI("pipeline!!! destory!!!!!\n %d", (int)node); + if (!node || !node->opaque) + return; + IJKFF_Pipenode_Opaque *opaque = node->opaque; + if (opaque->context) { + dealloc_videotoolbox(opaque->context); + } +} + +static int func_run_sync(IJKFF_Pipenode *node) +{ + IJKFF_Pipenode_Opaque *opaque = node->opaque; + return videotoolbox_video_thread(opaque); +} + + + +IJKFF_Pipenode *ffpipenode_create_video_decoder_from_ios_videotoolbox(FFPlayer *ffp) +{ + if (!ffp || !ffp->is) + return NULL; + + IJKFF_Pipenode *node = ffpipenode_alloc(sizeof(IJKFF_Pipenode_Opaque)); + if (!node) + return node; + memset(node, sizeof(IJKFF_Pipenode), 0); + + VideoState *is = ffp->is; + IJKFF_Pipenode_Opaque *opaque = node->opaque; + node->func_destroy = func_destroy; + node->func_run_sync = func_run_sync; + opaque->ffp = ffp; + opaque->decoder = &is->viddec; + opaque->avctx = opaque->decoder->avctx; + switch (opaque->avctx->codec_id) { + case AV_CODEC_ID_H264: + opaque->context = init_videotoolbox(ffp, opaque->avctx); + break; + default: + ALOGI("Videotoolbox-pipeline:open_video_decoder: not H264\n"); + goto fail; + } + if (opaque->context == NULL) { + ALOGE("could not init video tool box decoder !!!"); + goto fail; + } + return node; + +fail: + ffpipenode_free_p(&node); + return NULL; +} diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.h new file mode 100644 index 00000000..8acc9d4f --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.h @@ -0,0 +1,32 @@ +/* + * ffpipenode_ios_videotoolbox_vout.h + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FFPLAY__FF_FFPIPENODE_IOS_VIDEOTOOLBOX_VOUT_H +#define FFPLAY__FF_FFPIPENODE_IOS_VIDEOTOOLBOX_VOUT_H + +#include "ijkplayer/ff_ffpipenode.h" + +typedef struct FFPlayer FFPlayer; + +IJKFF_Pipenode *ffpipenode_create_video_output_from_ios_videotoolbox(FFPlayer *ffp); + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.m new file mode 100644 index 00000000..5375a2c0 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijkplayer/ios/pipeline/ffpipenode_ios_videotoolbox_vout.m @@ -0,0 +1,55 @@ +/* + * ffpipenode_ios_videotoolbox_vout.m + * + * Copyright (c) 2014 Zhou Quan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ffpipenode_ios_videotoolbox_vout.h" +#include "ff_ffpipeline.h" +#include "IJKVideoToolBox.h" +#include "ijkplayer/ff_ffpipenode.h" +#include "ijkplayer/ff_ffplay.h" + +typedef struct IJKFF_Pipenode_Opaque { + FFPlayer *ffp; +} IJKFF_Pipenode_Opaque; + +static void func_destroy(IJKFF_Pipenode *node) +{ + // do nothing +} + +static int func_run_sync(IJKFF_Pipenode *node) +{ + IJKFF_Pipenode_Opaque *opaque = node->opaque; + int ret = ffp_video_refresh_thread(opaque->ffp); + return ret; +} + +IJKFF_Pipenode *ffpipenode_create_video_output_from_ios_videotoolbox(FFPlayer *ffp) +{ + IJKFF_Pipenode *node = ffpipenode_alloc(sizeof(IJKFF_Pipenode_Opaque)); + if (!node) + return node; + IJKFF_Pipenode_Opaque *opaque = node->opaque; + opaque->ffp = ffp; + node->func_destroy = func_destroy; + node->func_run_sync = func_run_sync; + return node; +} diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.h index 8f6b6100..4d2cf112 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.h +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.h @@ -1,9 +1,7 @@ /* * IJKSDLGLRenderNV12.h * - * Copyright (c) 2014 Zhang Rui - * - * based on https://github.com/kolyvan/kxmovie + * Copyright (c) 2014 Zhou Quan * * This file is part of ijkPlayer. * diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.m index 380bbcf8..c6753f58 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/IJKSDLGLRenderNV12.m @@ -1,9 +1,7 @@ /* * IJKSDLGLRenderNV12.m * - * Copyright (c) 2014 Zhou Quan - * - * based on https://github.com/kolyvan/kxmovie + * Copyright (c) 2014 Zhou Quan * * This file is part of ijkPlayer. * @@ -55,9 +53,9 @@ static NSString *const g_nv12FragmentShaderString = IJK_SHADER_STRING // BT.709, which is the standard for HDTV. static const GLfloat kColorConversion709[] = { - 1.164, 1.164, 1.164, - 0.0, -0.213, 2.112, - 1.793, -0.533, 0.0, + 1.164, 1.164, 1.164, + 0.0, -0.213, 2.112, + 1.793, -0.533, 0.0, }; @@ -93,6 +91,11 @@ static const GLfloat kColorConversion709[] = { assert(overlay->format == SDL_FCC_NV12); assert(overlay->planes == 2); + if (overlay->pixels[0] == NULL || overlay->pixels[1] == NULL) + { + return; + } + const NSUInteger frameHeight = overlay->h; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_ios_gles2.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_ios_gles2.m index ff1876b3..24cd9445 100644 --- a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_ios_gles2.m +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_ios_gles2.m @@ -28,6 +28,8 @@ #include "ijksdl/ijksdl_vout_internal.h" #include "ijksdl/ffmpeg/ijksdl_vout_overlay_ffmpeg.h" #import "IJKSDLGLView.h" +#include "ijksdl_vout_overlay_videotoolbox.h" +#import "IJKVideoToolBox.h" typedef struct SDL_VoutSurface_Opaque { SDL_Vout *vout; @@ -39,7 +41,14 @@ typedef struct SDL_Vout_Opaque { static SDL_VoutOverlay *vout_create_overlay_l(int width, int height, Uint32 format, SDL_Vout *vout) { - return SDL_VoutFFmpeg_CreateOverlay(width, height, format, vout); + if (format == SDL_FCC_NV12) + { + return SDL_VoutVideoToolBox_CreateOverlay(width, height, format, vout); + } + else + { + return SDL_VoutFFmpeg_CreateOverlay(width, height, format, vout); + } } static SDL_VoutOverlay *vout_create_overlay(int width, int height, Uint32 format, SDL_Vout *vout) @@ -109,7 +118,6 @@ SDL_Vout *SDL_VoutIos_CreateForGLES2() SDL_Vout_Opaque *opaque = vout->opaque; opaque->gl_view = nil; - vout->create_overlay = vout_create_overlay; vout->free_l = vout_free_l; vout->display_overlay = voud_display_overlay; diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.h b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.h new file mode 100644 index 00000000..f9b235f4 --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * ijksdl_vout_overlay_videotoolbox.h + ***************************************************************************** + * + * copyright (c) 2014 ZhouQuan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IJKMediaPlayer__ijksdl_vout_overlay_videotoolbox__ +#define __IJKMediaPlayer__ijksdl_vout_overlay_videotoolbox__ + +#include "ijksdl_stdinc.h" +#include "ijksdl_vout.h" +#include "IJKVideoToolBox.h" +#include "ijksdl_inc_ffmpeg.h" + +SDL_VoutOverlay *SDL_VoutVideoToolBox_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *vout); +int SDL_VoutOverlayVideoToolBox_FillFrame(SDL_VoutOverlay *overlay, VTBPicture* picture); + +#endif diff --git a/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.m b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.m new file mode 100644 index 00000000..5d69127f --- /dev/null +++ b/ios/IJKMediaPlayer/IJKMediaPlayer/ijkmedia/ijksdl/ios/ijksdl_vout_overlay_videotoolbox.m @@ -0,0 +1,145 @@ +/***************************************************************************** + * ijksdl_vout_overlay_videotoolbox.m + ***************************************************************************** + * + * copyright (c) 2014 ZhouQuan + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ijksdl_vout_overlay_videotoolbox.h" + +#include +#include "ijksdl_stdinc.h" +#include "ijksdl_mutex.h" +#include "ijksdl_vout_internal.h" +#include "ijksdl_video.h" +#include "IJKVideoToolBox.h" + + +typedef struct SDL_VoutOverlay_Opaque { + SDL_mutex *mutex; + CVBufferRef pixBuffer; + Uint16 pitches[AV_NUM_DATA_POINTERS]; + Uint8 *pixels[AV_NUM_DATA_POINTERS]; +} SDL_VoutOverlay_Opaque; + + +static void overlay_free_l(SDL_VoutOverlay *overlay) +{ + if (!overlay) + return; + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + if (!opaque) + return; + overlay->unref(overlay); + if (opaque->mutex) + SDL_DestroyMutex(opaque->mutex); + + SDL_VoutOverlay_FreeInternal(overlay); +} + + +int SDL_VoutOverlayVideoToolBox_FillFrame(SDL_VoutOverlay *overlay, VTBPicture* picture) +{ + CVBufferRef pixBuffer = CVBufferRetain(picture->cvBufferRef); + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + if (opaque->pixBuffer != NULL) { + CVBufferRelease(opaque->pixBuffer); + } + opaque->pixBuffer = pixBuffer; + overlay->format = SDL_FCC_NV12; + overlay->planes = 2; + + if (CVPixelBufferLockBaseAddress(pixBuffer, 0) != kCVReturnSuccess) { + overlay->pixels[0] = NULL; + overlay->pixels[1] = NULL; + overlay->pitches[0] = 0; + overlay->pitches[1] = 0; + overlay->w = 0; + overlay->h = 0; + CVBufferRelease(pixBuffer); + opaque->pixBuffer = NULL; + return -1; + } + overlay->pixels[0] = CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0); + overlay->pixels[1] = CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 1); + overlay->pitches[0] = CVPixelBufferGetBytesPerRowOfPlane(pixBuffer, 0); + overlay->pitches[1] = CVPixelBufferGetBytesPerRowOfPlane(pixBuffer, 1); + overlay->w = (int)picture->width; + overlay->h = (int)picture->height; + CVPixelBufferUnlockBaseAddress(pixBuffer, 0); + + return 0; +} + +static int overlay_lock(SDL_VoutOverlay *overlay) +{ + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + return SDL_LockMutex(opaque->mutex); +} + +static int overlay_unlock(SDL_VoutOverlay *overlay) +{ + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + return SDL_UnlockMutex(opaque->mutex); +} + +static void overlay_unref(SDL_VoutOverlay *overlay) +{ + if (!overlay) { + return; + } + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + if (!opaque) { + return; + } + + CVBufferRelease(opaque->pixBuffer); + + opaque->pixBuffer = NULL; + overlay->pixels[0] = NULL; + overlay->pixels[1] = NULL; + + return; +} + +SDL_VoutOverlay *SDL_VoutVideoToolBox_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *display) +{ + SDLTRACE("SDL_VoutVideoToolBox_CreateOverlay(w=%d, h=%d, fmt=%.4s(0x%x, dp=%p)\n", width, height, (const char*) &format, format, display); + SDL_VoutOverlay *overlay = SDL_VoutOverlay_CreateInternal(sizeof(SDL_VoutOverlay_Opaque)); + if (!overlay) { + ALOGE("overlay allocation failed"); + return NULL; + } + SDL_VoutOverlay_Opaque *opaque = overlay->opaque; + overlay->format = format; + overlay->w = width; + overlay->h = height; + overlay->pitches = opaque->pitches; + overlay->pixels = opaque->pixels; + overlay->free_l = overlay_free_l; + overlay->lock = overlay_lock; + overlay->unlock = overlay_unlock; + overlay->unref = overlay_unref; + opaque->mutex = SDL_CreateMutex(); + return overlay; + +fail: + overlay_free_l(overlay); + return NULL; +} -- GitLab