如何成为杰出的程序员或怎样成为软件工程师师

智能手机教程子分类据群众举报,经缜密侦查,共查获嫌疑人数百名。
女子在郑州高速上被踹下车,哭诉一年被打二十次。
声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
  微信朋友圈里的小视频,是个非常有用的功能,可以向大家展示自己的所见,表达力远远超过文字和图片,当然视频多了也难免造成骚扰,而且似乎更费流量。
  那么,微信是如何设计小视频功能的呢?
  首先,微信小视频的设计初衷,是为了帮助用户更好地沟通,解决文字表述困难的问题,比如说跟朋友去吃饭,可能会这样的对话:
  微信6.0版将小视频作为核心亮点之一,宣告视频时代的到来。到如今,大量用户已经开始习惯用小视频分享生活中的新鲜和感动。不用再傻乎乎地打字,不用再考虑表达,小视频帮你做到”所见即所得”。
  但是,你知道么,为什么小视频最长只能录6秒?
  很多时候,用户常在户外使用微信,网络不稳定,流量也受限,不太适合用户发布或者查看大体积的视频文件。对视频时长做限制(不超过6秒),并做优化,使其体积变得更小,才更利于在移动端发布和观看。
  将小视频处理为6秒,主要是为了降低接收方的流量压力和播放时的心理压力。实际上,在小视频正式发布前,微信的“攻城狮”进行了长达半年的体验测试,发现6秒是最适合的长度。
  为了做测试,开发人员还专门撰写了一个名为“kitchen”的调试工具,作为一个全局开关变量配置的功能模块,改一个数值,配置立刻生效,方便调试。这就像一个PS工具,帮助开发人员找到最优的路径。6秒长度正是在这个工具的支持下发现的最适合长度。
  既然是小视频,那么小视频的流量消耗有多小?
  有一个很简单的换算:聊天中的小视频,每条视频的大小约等于2张朋友圈图片大小,朋友圈中的小视频每条大约等于3张朋友圈图片大小。
  和长视频相比,6秒小视频的数据量在iOS系统下为400KB以内,在Android系统为200-300KB,这意味着,三条小视频的流量消耗约为1MB。
  而且,小视频加载的逻辑是看过的不再加载,没有看的不加载,正在看的才加载,会主动帮用户省流量。
  所以,无论是拍小视频还是看小视频都不要有压力。按现在的流量费用标准,比如中国移动的500MB套餐30元,三段小视频才1MB,约合0.06元。
  问题来了,小视频这么小,还能清晰么?
  这就要说到码率和分辨率这两个与视觉的“清晰感”息息相关的参数了。
  码率决定了视频的质量,原始码率越高则视频越清晰,而分辨率则决定了视频图像的可放大倍数。其中码率有以下原则:
  A、码率和视频质量成正比,但是文件体积也和码率成正比。
  B、码率超过一定数值,对图像的质量没有多大影响。
  和长视频相比,小视频的视频编码算法其实并没有特别大区别。但是由于限定在6秒以内,体积则大大减小,而且主要在移动端播放,微信在视频质量和大小之间找到了一个平衡点,让用户毫无压力地使用。
  其实,小视频用起来方便,交互体验好,还不仅仅是流量耗费小,打开无压力。为了得到更好的交互体验,在拍摄录制与播放中,“小视频”还有很多你不知道的小秘密,比如:
  预启动――按下就录上了。小视频按下就开始录制,不用等待音频设备和文件的准备。这样就可以保证在稍纵即逝的当下捕捉到想要捕捉的内容。想象一下每次拍照时的争分夺秒,就知道这个小功能多重要了。
  抖动监测――你抖快了有提醒。为了保证拍摄的效果,根据陀螺仪的加速度变化,系统会自动检测到手机的抖动,并做提醒。
  暗光环境监测――太暗了就给选择调亮些。当拍摄环境过暗,小视频会提示可使用“点亮模式”来优化拍摄环境。这么智能是如何实现呢?
  实际上,视频中每个像素的RGB颜色值都不一样,为了快速处理和反应,通过将其缩放,获得一个平均值,当这个RGB平均值低于一定的设定值,如小于17时,就弹出提示,从而快速分辨周边环境的明暗。
  静音播放――点击放大才有声音。小视频只有在点击放大时才是带声音播放,并且循环播放,接近GIF的效果。而当非全屏时,录制的小视频保持静音播放状态。
  在iOS版本优化的过程中,微信团队设计过多个播放技术方案,最终选择了绕过iOS系统播放器同时最多只能有16个的限制,而采用自己开发的视频播放器,来满足可以同时播放多个视频等需求,并且为了防止拍摄播放时卡住界面的情况,在视频录制时加入Cache层,先缓存视频帧,后续等CPU空闲时再唤起后台线程写入视频。
  这些费心的“小动作”可以让你完全不用关注和懂得什么对焦、缩放、曝光补偿、白平衡……等等,你只需要拿起手机拍即可。
  微信6.0带来了小视频,无论是看精彩节目,还是在享受美食,或者生命中其他诸多感怀的时刻,你都可以用一段小视频,把那种身临其境分享出来。
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。
请先登录再操作
请先登录再操作
微信扫一扫分享至朋友圈
搜狐公众平台官方账号
生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者
搜狐网教育频道官方账号
全球最大华文占星网站-专业研究星座命理及测算服务机构
主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓
主演:尚格?云顿/乔?弗拉尼甘/Bianca Bree
主演:艾斯?库珀/ 查宁?塔图姆/ 乔纳?希尔
baby14岁写真曝光
李冰冰向成龙撒娇争宠
李湘遭闺蜜曝光旧爱
美女模特教老板走秀
曝搬砖男神奇葩择偶观
柳岩被迫成赚钱工具
大屁小P虐心恋
匆匆那年大结局
乔杉遭粉丝骚扰
男闺蜜的尴尬初夜
客服热线:86-10-
客服邮箱:  本文个人原创,转载请注明出处,谢谢。
  前段时间项目要求需要在聊天模块中加入类似微信的小视频功能,这边博客主要是为了总结遇到的问题和解决方法,希望能够对有同样需求的朋友有所帮助。
  效果预览:
  这里先罗列遇到的主要问题:  
视频剪裁 &微信的小视频只是取了摄像头获取的一部分画面
滚动预览的卡顿问题 &AVPlayer播放视频在滚动中会出现很卡的问题
& & & 接下来让我们一步步来实现。
Part 1 实现视频录制
  1.录制类WKMovieRecorder实现
  创建一个录制类WKMovieRecorder,负责视频录制。
@interface WKMovieRecorder : NSObject
+ (WKMovieRecorder*) sharedR
& - (instancetype)initWithMaxDuration:(NSTimeInterval)
  定义回调block
@param info
@param isCancle YES:取消 NO:正常结束
typedef void(^FinishRecordingBlock)(NSDictionary *info, WKRecorderFinishedReason finishReason);
typedef void(^FocusAreaDidChanged)();
@param success 是否成功
typedef void(^AuthorizationResult)(BOOL success);
@interface WKMovieRecorder : NSObject
@property (nonatomic, copy) FinishRecordingBlock finishB//录制结束回调
@property (nonatomic, copy) FocusAreaDidChanged focusAreaDidChangedB
@property (nonatomic, copy) AuthorizationResult authorizationResultB
  定义一个cropSize用于视频裁剪
@property (nonatomic, assign) CGSize cropS
  接下来就是capture的实现了,这里代码有点长,懒得看的可以直接看后面的视频剪裁部分
  录制配置:
@interface WKMovieRecorder ()
AVCaptureVideoDataOutputSampleBufferDelegate,
AVCaptureAudioDataOutputSampleBufferDelegate,
WKMovieWriterDelegate
AVCaptureSession* _
AVCaptureVideoPreviewLayer* _
WKMovieWriter* _
//暂停录制
int _currentF
CMTime _timeO
CMTime _lastV
CMTime _lastA
NSTimeInterval _maxD
// Session management.
@property (nonatomic, strong) dispatch_queue_t sessionQ
@property (nonatomic, strong) dispatch_queue_t videoDataOutputQ
@property (nonatomic, strong) AVCaptureSession *
@property (nonatomic, strong) AVCaptureDevice *captureD
@property (nonatomic, strong) AVCaptureDeviceInput *videoDeviceI
@property (nonatomic, strong) AVCaptureStillImageOutput *stillImageO
@property (nonatomic, strong) AVCaptureConnection *videoC
@property (nonatomic, strong) AVCaptureConnection *audioC
@property (nonatomic, strong) NSDictionary *videoCompressionS
@property (nonatomic, strong) NSDictionary *audioCompressionS
@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor *
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataO
//Utilities
@property (nonatomic, strong) NSMutableArray *//存储录制帧
@property (nonatomic, assign) CaptureAVSetupR
@property (atomic, readwrite) BOOL isC
@property (atomic, readwrite) BOOL isP
@property (nonatomic, strong) NSTimer *durationT
@property (nonatomic, assign) WKRecorderFinishedReason finishR
  实例化方法:
+ (WKMovieRecorder *)sharedRecorder
static WKMovieRecorder *
static dispatch_once_t onceT
dispatch_once(&onceToken, ^{
recorder = [[WKMovieRecorder alloc] initWithMaxDuration:CGFLOAT_MAX];
- (instancetype)initWithMaxDuration:(NSTimeInterval)duration
if(self = [self init]){
_maxDuration =
_duration = 0.f;
- (instancetype)init
self = [super init];
if (self) {
_maxDuration = CGFLOAT_MAX;
_duration = 0.f;
_sessionQueue = dispatch_queue_create("wukong.movieRecorder.queue", DISPATCH_QUEUE_SERIAL );
_videoDataOutputQueue = dispatch_queue_create( "wukong.movieRecorder.video", DISPATCH_QUEUE_SERIAL );
dispatch_set_target_queue( _videoDataOutputQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
  2.初始化设置
  初始化设置分别为session创建、权限检查以及session配置
  1.session创建
self.session = [[AVCaptureSession alloc] init];
self.result = CaptureAVSetupResultS
  2.权限检查
//权限检查
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
self.result = CaptureAVSetupResultS
case AVAuthorizationStatusAuthorized: {
self.result = CaptureAVSetupResultCameraNotA
if ( self.result != CaptureAVSetupResultSuccess) {
if (self.authorizationResultBlock) {
self.authorizationResultBlock(NO);
  3.session配置
  session配置是需要注意的是AVCaptureSession的配置不能在主线程, 需要自行创建串行线程。
  3.1.1 获取输入设备与输入流
 AVCaptureDevice *captureDevice = [[self class] deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
_captureDevice = captureD
NSError *error =
_videoDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
if (!_videoDeviceInput) {
NSLog(@"未找到设备");
  3.1.2 录制帧数设置
  帧数设置的主要目的是适配iPhone4,毕竟是应该淘汰的机器了
       int frameR
if ( [NSProcessInfo processInfo].processorCount == 1 )
if ([self.session canSetSessionPreset:AVCaptureSessionPresetLow]) {
[self.session setSessionPreset:AVCaptureSessionPresetLow];
frameRate = 10;
if ([self.session canSetSessionPreset:AVCaptureSessionPreset640x480]) {
[self.session setSessionPreset:AVCaptureSessionPreset640x480];
frameRate = 30;
CMTime frameDuration = CMTimeMake( 1, frameRate );
if ( [_captureDevice lockForConfiguration:&error] ) {
_captureDevice.activeVideoMaxFrameDuration = frameD
_captureDevice.activeVideoMinFrameDuration = frameD
[_captureDevice unlockForConfiguration];
NSLog( @"videoDevice lockForConfiguration returned error %@", error );
  3.1.3 视频输出设置
  视频输出设置需要注意的问题是:要设置videoConnection的方向,这样才能保证设备旋转时的显示正常。
      
if ([self.session canAddInput:_videoDeviceInput]) {
[self.session addInput:_videoDeviceInput];
self.videoDeviceInput = _videoDeviceI
[self.session removeOutput:_videoDataOutput];
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
_videoDataOutput = videoO
videoOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
[videoOutput setSampleBufferDelegate:self queue:_videoDataOutputQueue];
videoOutput.alwaysDiscardsLateVideoFrames = NO;
if ( [_session canAddOutput:videoOutput] ) {
[_session addOutput:videoOutput];
[_captureDevice addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:FocusAreaChangedContext];
_videoConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
if(_videoConnection.isVideoStabilizationSupported){
_videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeA
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarO
AVCaptureVideoOrientation initialVideoOrientation = AVCaptureVideoOrientationP
if ( statusBarOrientation != UIInterfaceOrientationUnknown ) {
initialVideoOrientation = (AVCaptureVideoOrientation)statusBarO
_videoConnection.videoOrientation = initialVideoO
NSLog(@"无法添加视频输入到会话");
  3.1.4 音频设置
  需要注意的是为了不丢帧,需要把音频输出的回调队列放在串行队列中
       //audio
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
if ( ! audioDeviceInput ) {
NSLog( @"Could not create audio device input: %@", error );
if ( [self.session canAddInput:audioDeviceInput] ) {
[self.session addInput:audioDeviceInput];
NSLog( @"Could not add audio device input to the session" );
AVCaptureAudioDataOutput *audioOut = [[AVCaptureAudioDataOutput alloc] init];
// Put audio on its own queue to ensure that our video processing doesn't cause us to drop audio
dispatch_queue_t audioCaptureQueue = dispatch_queue_create( "wukong.movieRecorder.audio", DISPATCH_QUEUE_SERIAL );
[audioOut setSampleBufferDelegate:self queue:audioCaptureQueue];
if ( [self.session canAddOutput:audioOut] ) {
[self.session addOutput:audioOut];
_audioConnection = [audioOut connectionWithMediaType:AVMediaTypeAudio];
  还需要注意一个问题就是对于session的配置代码应该是这样的
[self.session beginConfiguration];
...配置代码
[self.session commitConfiguration];
  由于篇幅问题,后面的录制代码我就挑重点的讲了。
  3.2 &视频存储
  现在我们需要在AVCaptureVideoDataOutputSampleBufferDelegate与AVCaptureAudioDataOutputSampleBufferDelegate的回调中,将音频和视频写入沙盒。在这个过程中需要注意的,在启动session后获取到的第一帧黑色的,需要放弃。
  3.2.1 创建WKMovieWriter类来封装视频存储操作
  WKMovieWriter的主要作用是利用AVAssetWriter拿到CMSampleBufferRef,剪裁后再写入到沙盒中。
  这是剪裁配置的代码,AVAssetWriter会根据cropSize来剪裁视频,这里需要注意的一个问题是cropSize的width必须是320的整数倍,不然的话剪裁出来的视频右侧会出现一条绿色的线
  NSDictionary *videoS
if (_cropSize.height == 0 || _cropSize.width == 0) {
_cropSize = [UIScreen mainScreen].bounds.
videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:_cropSize.width], AVVideoWidthKey,
[NSNumber numberWithInt:_cropSize.height], AVVideoHeightKey,
AVVideoScalingModeResizeAspectFill,AVVideoScalingModeKey,
  至此,视频录制就完成了。
  接下来需要解决的预览的问题了
Part 2 卡顿问题解决
  1.1 gif图生成
  通过查资料发现了这篇&介绍说微信团队解决预览卡顿的问题使用的是播放图片gif,但是博客中的示例代码有问题,通过CoreAnimation来播放图片导致内存暴涨而crash。但是,还是给了我一些灵感,因为之前项目的启动页用到了gif图片的播放,所以我就想能不能把视频转成图片,然后再转成gif图进行播放,这样不就解决了问题了吗。于是我开始google功夫不负有心人找到了,图片数组转gif图片的。
  gif图转换代码
static void makeAnimatedGif(NSArray *images, NSURL *gifURL, NSTimeInterval duration) {
NSTimeInterval perSecond = duration /images.
NSDictionary *fileProperties = @{
(__bridge id)kCGImagePropertyGIFDictionary: @{
(__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever
NSDictionary *frameProperties = @{
(__bridge id)kCGImagePropertyGIFDictionary: @{
(__bridge id)kCGImagePropertyGIFDelayTime: @(perSecond), // a float (not double!) in seconds, rounded to centiseconds in the GIF data
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)gifURL, kUTTypeGIF, images.count, NULL);
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
for (UIImage *image in images) {
@autoreleasepool {
CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
if (!CGImageDestinationFinalize(destination)) {
NSLog(@"failed to finalize image destination");
CFRelease(destination);
  转换是转换成功了,但是出现了新的问题,使用ImageIO生成gif图片时会导致内存暴涨,瞬间涨到100M以上,如果多个gif图同时生成的话一样会crash掉,为了解决这个问题需要用一个串行队列来进行gif图的生成  
  1.2 视频转换为UIImages
  主要是通过AVAssetReader、AVAssetTrack、AVAssetReaderTrackOutput 来进行转换
//转成UIImage
- (void)convertVideoUIImagesWithURL:(NSURL *)url finishBlock:(void (^)(id images, NSTimeInterval duration))finishBlock
AVAsset *asset = [AVAsset assetWithURL:url];
NSError *error =
self.reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
NSTimeInterval duration = CMTimeGetSeconds(asset.duration);
__weak typeof(self)weakSelf =
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(backgroundQueue, ^{
__strong typeof(weakSelf) strongSelf = weakS
NSLog(@"");
if (error) {
NSLog(@"%@", [error localizedDescription]);
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack =[videoTracks firstObject];
if (!videoTrack) {
int m_pixelFormatT
视频播放时,
m_pixelFormatType = kCVPixelFormatType_32BGRA;
// 其他用途,如视频压缩
m_pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoR
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:@(m_pixelFormatType) forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:options];
if ([strongSelf.reader canAddOutput:videoReaderOutput]) {
[strongSelf.reader addOutput:videoReaderOutput];
[strongSelf.reader startReading];
NSMutableArray *images = [NSMutableArray array];
// 要确保nominalFrameRate&0,之前出现过android拍的0帧视频
while ([strongSelf.reader status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate & 0) {
@autoreleasepool {
// 读取 video sample
CMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];
if (!videoBuffer) {
[images addObject:[WKVideoConverter convertSampleBufferRefToUIImage:videoBuffer]];
CFRelease(videoBuffer);
if (finishBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
finishBlock(images, duration);
  在这里有一个值得注意的问题,在视频转image的过程中,由于转换时间很短,在短时间内videoBuffer不能够及时得到释放,在多个视频同时转换时任然会出现内存问题,这个时候就需要用autoreleasepool来实现及时释放
@autoreleasepool {
// 读取 video sample
CMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];
  if (!videoBuffer) {
  [images addObject:[WKVideoConverter convertSampleBufferRefToUIImage:videoBuffer]];
   CFRelease(videoBuffer); }
  至此,微信小视频的难点(我认为的)就解决了,至于其他的实现代码请看demo就基本实现了,demo可以从下载,如果觉得对你有帮助的话,帮我star一下哟,感激不尽  
  UIImages转gif图 &/sahara108/6772691
  视频暂停录制& http://www.gdcl.co.uk//iPhone-Pause.html
  视频crop绿边解决&/questions//avassetexportsession-giving-me-a-green-border-on-right-and-bottom-of-output-vide
  视频裁剪:/questions//video-capture-with-11-aspect-ratio-in-ios/10263
  CMSampleBufferRef转image&/library/ios/qa/qa1702/_index.html
  微信小视频分析&/p/3d5ccbde0de1
感谢以上文章的作者
阅读(...) 评论()

我要回帖

更多关于 软件工程师 程序员 的文章

 

随机推荐