一、引言

Quick Look技术是Apple在Mac OS X 10.5中引入的一种用于快速查看文件内容的技术。用户只需要选中文件单击空格键即可快速查看文件内容,可以在不打开文件的情况下快速浏览内容。公司是做全景视频开发的,具备自己的全景视频文件格式。因此,做一款针对自有视频格式的QuickLook插件显得非常有必要。QuickLook的技术资料非常丰富,不仅官方有着详尽的文档,互联网上也有不少开发者总结的开发经验。即便如此,在开发的过程中也碰到了不少的坑,如今总结在此。最终的QuickLook效果如下所示:

     

二、着手开发

QuickLook插件是Apple官方推出的一项技术,在XCode中可以直接创建QuickLook Plugins模板工程:

创建的模板工程中包含三个源文件:GenerateThumbnailForURL.c, GeneratePreviewForURL.c, main.c。其作用可顾名思义,不过我们只要修改前面两个文件就可以了。至于main.c文件,官方是不推荐我们去修改的。GenerateThumbnailForURL.c用于生成缩略图,如下是一种模板实现:

OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
{
@autoreleasepool
{
// Get the UTI properties
NSDictionary* uti_declarations = (__bridge_transfer NSDictionary*)UTTypeCopyDeclaration(contentTypeUTI); // Get the extensions corresponding to the image UTI, for some UTI there can be more than 1 extension (ex image.jpeg = jpeg, jpg...)
// If it fails for whatever reason fallback to the filename extension
id extensions = uti_declarations[(__bridge NSString*)kUTTypeTagSpecificationKey][(__bridge NSString*)kUTTagClassFilenameExtension];
NSString* extension = ([extensions isKindOfClass:[NSArray class]]) ? extensions[0] : extensions;
if (nil == extension)
extension = ([(__bridge NSURL*)url pathExtension] != nil) ? [(__bridge NSURL*)url pathExtension] : @"";
extension = [extension lowercaseString]; // Create the properties dic
CFTypeRef keys[1] = {kQLThumbnailPropertyExtensionKey};
CFTypeRef values[1] = {(__bridge CFStringRef)extension};
CFDictionaryRef properties = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // Check by extension because it's highly unprobable that an UTI for these formats is declared
// the simplest way to declare one is creating a dummy automator app and adding imported/exported UTI conforming to public.image
CGImageRef img_ref = NULL;
CFStringRef filepath = CFURLCopyPath(url);
if ([extension isEqualToString:@"insp"])
{
if (!QLThumbnailRequestIsCancelled(thumbnail))
{
// 1. decode the image
img_ref = decode_insp_at_path(filepath, NULL);
if (filepath != NULL)
CFRelease(filepath); // 2. render it
if (img_ref != NULL)
{
QLThumbnailRequestSetImage(thumbnail, img_ref, properties);
CGImageRelease(img_ref);
}
else
QLThumbnailRequestSetImageAtURL(thumbnail, url, properties);
}
else
QLThumbnailRequestSetImageAtURL(thumbnail, url, properties);
}
else if ([extension isEqualToString:@"insv"])
{
if (!QLThumbnailRequestIsCancelled(thumbnail))
{
// 1. decode the image
img_ref = decode_insv_at_path(filepath, NULL);
if (filepath != NULL)
CFRelease(filepath); // 2. render it
if (img_ref != NULL)
{
QLThumbnailRequestSetImage(thumbnail, img_ref, properties);
CGImageRelease(img_ref);
}
else
QLThumbnailRequestSetImageAtURL(thumbnail, url, properties);
}
else
QLThumbnailRequestSetImageAtURL(thumbnail, url, properties);
}
else
QLThumbnailRequestSetImageAtURL(thumbnail, url, properties); if (properties != NULL)
CFRelease(properties);
}
return noErr;
}

  基本流程为:先获取文件UTI(Uniform Type Identifiers),然后再根据文件的扩展名来过滤文件,继而调用相应的解码库对图片或视频进行解码,构建CGImage引用返回。

GeneratePreviewForURL.c文件则用于完成预览图的生成。缩略图是就用单击空格键时弹出来的图,基本模板代码如下:

OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
{
@autoreleasepool
{
NSString* extension = [[(__bridge NSURL*)url pathExtension] lowercaseString];
image_infos infos;
memset(&infos, 0, sizeof(image_infos));
CGImageRef img_ref = NULL;
CFStringRef filepath = CFURLCopyPath(url);
if ([extension isEqualToString:@"insp"])
{
// 1. decode the image
if (!QLPreviewRequestIsCancelled(preview))
{
img_ref = decode_insp_at_path(filepath, &infos);
if (filepath != NULL)
CFRelease(filepath); // 2. render it
CFDictionaryRef properties = create_properties(url, infos.filesize, infos.width, infos.height, true);
if (img_ref != NULL)
{
// Have to draw the image ourselves
CGContextRef ctx = QLPreviewRequestCreateContext(preview, (CGSize){.width = OUT_WIDTH, .height = OUT_HEIGHT+LOGO_HEIGHT}, YES, properties);
CGContextDrawImage(ctx, (CGRect){.origin = CGPointZero, .size.width = OUT_WIDTH, .size.height = OUT_HEIGHT+LOGO_HEIGHT}, img_ref);
QLPreviewRequestFlushContext(preview, ctx);
CGContextRelease(ctx);
CGImageRelease(img_ref);
}
else
QLPreviewRequestSetURLRepresentation(preview, url, contentTypeUTI, properties);
if (properties != NULL)
CFRelease(properties);
}
}
else if([extension isEqualToString:@"insv"])
{
// 1. decode the image
if (!QLPreviewRequestIsCancelled(preview))
{
img_ref = decode_insv_at_path(filepath, &infos);
if (filepath != NULL)
CFRelease(filepath); // 2. render it
CFDictionaryRef properties = create_properties(url, infos.filesize, infos.width, infos.height, true);
if (img_ref != NULL)
{
// Have to draw the image ourselves
CGContextRef ctx = QLPreviewRequestCreateContext(preview, (CGSize){.width = OUT_WIDTH, .height = OUT_HEIGHT+LOGO_HEIGHT}, YES, properties);
CGContextDrawImage(ctx, (CGRect){.origin = CGPointZero, .size.width = OUT_WIDTH, .size.height = OUT_HEIGHT+LOGO_HEIGHT}, img_ref);
QLPreviewRequestFlushContext(preview, ctx);
CGContextRelease(ctx);
CGImageRelease(img_ref);
}
else
QLPreviewRequestSetURLRepresentation(preview, url, contentTypeUTI, properties);
if (properties != NULL)
CFRelease(properties);
} }
else
{
// Standard images (supported by the OS by default)
size_t width = 0, height = 0, file_size = 0;
properties_for_file(url, &width, &height, &file_size); // Request preview with updated titlebar
CFDictionaryRef properties = create_properties(url, file_size, width, height, false);
QLPreviewRequestSetURLRepresentation(preview, url, contentTypeUTI, properties); if (properties != NULL)
CFRelease(properties);
}
}
return kQLReturnNoError;
}

  代码逻辑甚至更简单,其中的create_properties()函数用于给预览窗口添加宽高、图片名等信息。不需要的话可以去掉。上面的模板代码编写好之后,QuickLook插件的主要工作就是视频和图片的编解码了。编译好的QuickLook插件是一个以qlgenerator为扩展名的Bundle。官方推荐的安装位置有三个:(1)~/Library/QuickLook/:存放第三方开发的QuickLook插件,针对当前用户的,只有当前用户登录了才会加载插件。(2)/Library/QuickLook:存放第三方开发的QuickLook插件,这是针对所有用户起作用的。(3)/System/Library/QuickLook/:这里只存放苹果公司开发的QuickLook插件,所有用户都能用。可以根据自身需要将QuickLook插件安装到相应的位置。

三、注意事项

(1) 确定并设置文件UTI。QuickLook插件需要根据文件的UTI来关联,因此首要的一步是确定自有文件格式的UTI。那么怎么确定呢?其实有个命令可以查看文件的UTI元信息:mdls。kMDItemContentType即为文件的UTI信息。把获得的UTI添加到QuickLook工程当中的Info.plist文件中去即可。

(2)日志路径。在开发QuickLook插件的过程中,难免会遇到崩溃的情况。这个时候日志是非常重要的一种调试手段。QuickLook插件出现异常的第一步应该去查看/var/log/system.log文件。这个是OSX系统的系统日志文件。QuickLook插件如出现加载异常现象,里面都会有记录。其次我们应该去/Users/[user name]/Library/Logs/DiagnosticReports/目录下去查看崩溃日志。

(3) 需要注意的是,在GeneratePreviewForURL()GenerateThumbnailForURL()方法中传进来的文件路径以URL形式存在的。也就是说,当路径中存在中文时,会进行URL Encode编码。也就是说,中文"直播间"会变成"%E7%9B%B4%E6%92%AD%E9%97%B4"。在解析的时候要注意进行URL Decode操作,否则的话无法读取到文件。

(4)qlmanage的使用。qlmanage可以用于清除QuickLook缓存,也可以用来查看当前系统中存在哪些QuickLook插件,也可以查看哪些文件是和哪些QuickLook插件关联的。这对于判断你的QuickLook插件是否起作用很方便:

(5) install_name_toolutool。这两个命令配合使用,主要用于修改动态库的链接路径。主要使用方法为:

    • utool -L *.dylib
    • install_name_tool -id "new_path" *.dylib
    • install_name_tool -change "old_path" "new_path" *.dylib   

四、参考链接

  1. http://blog.10to1.be/cocoa/2012/01/27/creating-a-quick-look-plugin/
  2. http://apple.stackexchange.com/questions/153595/how-can-i-see-which-quicklook-plugin-is-responsible-for-which-data-type
  3. https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/Quicklook_Programming_Guide/Introduction/Introduction.html

Mac OS X平台下QuickLook开发教程的更多相关文章

  1. Windows平台下PHP开发环境的配置

    Windows平台下PHP开发环境的配置 一.基本环境 1.Windows XP 32位 2.Apache 2.2.25,下载地址:http://mirror.bit.edu.cn/apache/ht ...

  2. 在Mac OS X中使用VIM开发STM32(2)

    本文原创于http://www.cnblogs.com/humaoxiao,非法转载者请自重! 在我先前的博文⎣在Mac OS X中使用VIM开发STM32(1)⎤中,我们安装完成了MACVIM,这一 ...

  3. 在Mac OS X中使用VIM开发STM32(1)

       本文原创于http://www.cnblogs.com/humaoxiao,非法转载者请自重!     在我先前的博文⎣在Mac OS X中搭建STM32开发环境⎤中,我们在Mac中DIY出了最 ...

  4. 转-在Mac OS上搭建Python的开发环境

    在Mac OS上搭建Python的开发环境   本文转载自:http://www.jb51.net/article/76931.htm 一. 安装python mac系统其实自带了一个python的执 ...

  5. 【开发软件】推荐一款MAC OS X 下php集成开发环境mamp

      这里给大家推荐一款在mac上搭建WEB服务器环境的集成环境安装软件,非常的好用,需要的朋友可以拿去,不用谢 ^_^   之前苦于mac上搭建本地服务器之艰辛,找寻好久都没找到一款类似windows ...

  6. 【转】windows环境下安装win8.1+Mac OS X 10.10双系统教程

    先要感谢远景论坛里的各位大神们的帖子  没有他们的分享我也不能顺利的装上Mac OS X 10.10! 写这篇随笔主要是为了防止自己遗忘,同时给大家分享下我的经验. 本教程适用于BIOS+MBR分区的 ...

  7. 笔记:MAC OS X下配置PHP开发、调试环境

    操作系统:MAC OS X 工具:MAMP.PhpStorm.xdebug.chrome 1.下载MAMP 2.安装比较简单,安装完成后,应用程序中会增加如下4个应用 MacGDBp是PHP调试器,使 ...

  8. windows环境下安装win8.1+Mac OS X 10.10双系统教程

    首先要感谢远景论坛里的各位大神们的帖子  没有他们的分享我也不能顺利的装上Mac OS X 10.10! 写这篇随笔主要是为了防止自己遗忘,同时给大家分享下我的经验. 本教程适用于BIOS+MBR分区 ...

  9. Mac OS X 懒人版安装教程(之前的图全部挂了,所以重发了)

    请版主把我之前发的那个帖子删了!因为所有的图全部挂了,所以麻烦版主了…… 安装中出现五国的话就请进入这里看看是那里的错误http://bbs.pcbeta.com/viewthread-863656- ...

随机推荐

  1. jQuery中设置form表单中action值与js有什么不同。。。。

    jQuery中设置form表单中action值与js有什么不同.... HTML代码如下: <form action="" method="post" i ...

  2. SQL Server表分区的NULL值问题

    SQL Server表分区的NULL值问题 SQL Server表分区只支持range分区这一种类型,但是本人觉得已经够用了 虽然MySQL支持四种分区类型:RANGE分区.LIST分区.HASH分区 ...

  3. ABP理论学习之EntityFramework集成

    返回总目录 本篇目录 Nuget包 创建DbContext 仓储 仓储基类 实现仓储 自定义仓储方法 阅读其他 ABP可以使用任何ORM框架工作,并且已经内置了EntityFramework集成.这篇 ...

  4. LibSVM for Python 使用

    经历手写SVM的惨烈教训(还是太年轻)之后,我决定使用工具箱/第三方库 Python libsvm的GitHub仓库 LibSVM是开源的SVM实现,支持C, C++, Java,Python , R ...

  5. 扩展Bootstrap Tooltip插件使其可交互

    最近在公司某项目开发中遇见一特殊需求,请笔者帮助,因此有了本文的插件.在前端开发中tooltip是一个极其常用的插件,它能更好向使用者展示更多的文档等帮助信息.它们通常都是一些静态文本信息.但同事他们 ...

  6. Docker学习笔记

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何 ...

  7. 关于ie11 的开发者工具

    win7旗舰系统64为,更新ie11: 新安装了ie11浏览器,安装以后发现原来可以正常使用的开发者工具不能使用,提示 Imposible use F12 Developer Tools (Excep ...

  8. SqlServer性能检测和优化工具使用详细

    工具概要 如果你的数据库应用系统中,存在有大量表,视图,索引,触发器,函数,存储过程,sql语句等等,又性能低下,而苦逼的你又要对其优化,那么你该怎么办?哥教你,首先你要知道问题出在哪里?如果想知道问 ...

  9. 【Win10 应用开发】OCR识别

    OCR,即Optical Character Recognition,光学字符识别.以下介绍来自搜索: OCR(Optical Character Recognition,光学字符识别)是指电子设备( ...

  10. java静态代理与动态代理

    原文链接:http://www.orlion.ga/207/ 一.代理模式 代理模式是经常用到的设计模式,代理模式是给指定对象提供代理对象.由代理对象来控制具体对象的引用. 代理模式涉及到的角色: 抽 ...