title: Add GIF watermark on a video in iOS
body: <p>I need to accomplish this function: There is a GIF overlay on a video, hoping to composition this video and GIF to a new video. I'm using the following code, but result is only the video without GIF:</p>
  
  <pre><code>- (void)mixVideoAsset:(AVAsset *)videoAsset {
  
      LLog(@"Begining");
  
      NSDate * begin = [NSDate date];
  
      // 2 - Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances.
      AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
  
      // 3 - Video track
      AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                          preferredTrackID:kCMPersistentTrackID_Invalid];
      [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
                          ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
                           atTime:kCMTimeZero error:nil];
  
      // - Audio
      AVMutableCompositionTrack *audioCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
      AVAssetTrack *audioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
      [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioTrack.timeRange.duration) ofTrack:audioTrack atTime:kCMTimeZero error:nil];
  
      // 3.1 - Create AVMutableVideoCompositionInstruction
      AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
      mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
  
      // 3.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation.
      AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
      AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
      UIImageOrientation videoAssetOrientation_  = UIImageOrientationUp;
      BOOL isVideoAssetPortrait_  = NO;
      CGAffineTransform videoTransform = videoAssetTrack.preferredTransform;
      if (videoTransform.a == 0 &amp;&amp; videoTransform.b == 1.0 &amp;&amp; videoTransform.c == -1.0 &amp;&amp; videoTransform.d == 0) {
          videoAssetOrientation_ = UIImageOrientationRight;
          isVideoAssetPortrait_ = YES;
      }
      if (videoTransform.a == 0 &amp;&amp; videoTransform.b == -1.0 &amp;&amp; videoTransform.c == 1.0 &amp;&amp; videoTransform.d == 0) {
          videoAssetOrientation_ =  UIImageOrientationLeft;
          isVideoAssetPortrait_ = YES;
      }
      if (videoTransform.a == 1.0 &amp;&amp; videoTransform.b == 0 &amp;&amp; videoTransform.c == 0 &amp;&amp; videoTransform.d == 1.0) {
          videoAssetOrientation_ =  UIImageOrientationUp;
      }
      if (videoTransform.a == -1.0 &amp;&amp; videoTransform.b == 0 &amp;&amp; videoTransform.c == 0 &amp;&amp; videoTransform.d == -1.0) {
          videoAssetOrientation_ = UIImageOrientationDown;
      }
      [videolayerInstruction setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];
      [videolayerInstruction setOpacity:0.0 atTime:videoAsset.duration];
  
      // 3.3 - Add instructions
      mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,nil];
  
      AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];
  
      CGSize naturalSize;
      if(isVideoAssetPortrait_){
          naturalSize = CGSizeMake(videoAssetTrack.naturalSize.height, videoAssetTrack.naturalSize.width);
      } else {
          naturalSize = videoAssetTrack.naturalSize;
      }
  
      float renderWidth, renderHeight;
      renderWidth = naturalSize.width;
      renderHeight = naturalSize.height;
      mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
      mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];
      mainCompositionInst.frameDuration = CMTimeMake(1, 30);
  
      // Watermark Layers
      [self applyVideoEffectsToComposition:mainCompositionInst size:naturalSize];
  
      // 4 - Get path
  //    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  //    NSString *documentsDirectory = [paths objectAtIndex:0];
  //    NSString *myPathDocs =  [documentsDirectory stringByAppendingPathComponent:
  //                             [NSString stringWithFormat:@"FinalVideo-%d.mov",arc4random() % 1000]];
  //    NSURL *url = [NSURL fileURLWithPath:myPathDocs];
      NSURL * url = TempVideoURL();
  
      // 5 - Create exporter
      AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                                        presetName:AVAssetExportPresetHighestQuality];
      exporter.outputURL=url;
      exporter.outputFileType = AVFileTypeMPEG4;
      exporter.shouldOptimizeForNetworkUse = YES;
      exporter.videoComposition = mainCompositionInst;
      [exporter exportAsynchronouslyWithCompletionHandler:^{
          dispatch_async(dispatch_get_main_queue(), ^{
              NSDate * endDate = [NSDate date];
              NSTimeInterval interval = [endDate timeIntervalSinceDate:begin];
              LLog(@"completed %f senconds",interval);
  
              ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
              if ([assetsLibrary videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
                  [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL];
              }
          });
      }];
  
  }
  </code></pre>
  
  <p>Add Gif Watermark</p>
  
  <pre><code>- (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition size:(CGSize)size
  {
  
      // - set up the parent layer
      CALayer *parentLayer = [CALayer layer];
      CALayer *videoLayer = [CALayer layer];
      parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
      videoLayer.frame = CGRectMake(0, 0, size.width, size.height);
      [parentLayer addSublayer:videoLayer];
  
      size.width = 100;
      size.height = 100;
  
      // - set up the overlay
      CALayer *overlayLayer = [CALayer layer];
      overlayLayer.frame = CGRectMake(0, 100, size.width, size.height);
  
      NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"jiafei" withExtension:@"gif"];
      [BBGifManager startGifAnimationWithURL:fileUrl inLayer:overlayLayer];
  
  //    UIImage * image = [UIImage imageNamed:@"gifImage.gif"];
  //    [overlayLayer setContents:(id)[image CGImage]];
  //    [overlayLayer setMasksToBounds:YES];
  
      [parentLayer addSublayer:overlayLayer];
  
      // - apply magic
      composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                   videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
  
  }
  </code></pre>
  
  <p>Add <code>CALayer</code> Animations</p>
  
  <pre><code>+ (void)startGifAnimationWithURL:(NSURL *)url inLayer:(CALayer *)layer {
      CAKeyframeAnimation * animation = [self animationForGifWithURL:url];
      [layer addAnimation:animation forKey:@"contents"];
  }
  </code></pre>
  
  <p>Create <code>CAKeyFrameAnimation</code></p>
  
  <pre><code>+ (CAKeyframeAnimation *)animationForGifWithURL:(NSURL *)url {
  
      CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
  
      NSMutableArray * frames = [NSMutableArray new];
      NSMutableArray *delayTimes = [NSMutableArray new];
  
      CGFloat totalTime = 0.0;
      CGFloat gifWidth;
      CGFloat gifHeight;
  
      CGImageSourceRef gifSource = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
  
      // get frame count
      size_t frameCount = CGImageSourceGetCount(gifSource);
      for (size_t i = 0; i &lt; frameCount; ++i) {
          // get each frame
          CGImageRef frame = CGImageSourceCreateImageAtIndex(gifSource, i, NULL);
          [frames addObject:(__bridge id)frame];
          CGImageRelease(frame);
  
          // get gif info with each frame
          NSDictionary *dict = (NSDictionary*)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(gifSource, i, NULL));
          NSLog(@"kCGImagePropertyGIFDictionary %@", [dict valueForKey:(NSString*)kCGImagePropertyGIFDictionary]);
  
          // get gif size
          gifWidth = [[dict valueForKey:(NSString*)kCGImagePropertyPixelWidth] floatValue];
          gifHeight = [[dict valueForKey:(NSString*)kCGImagePropertyPixelHeight] floatValue];
  
          // kCGImagePropertyGIFDictionary中kCGImagePropertyGIFDelayTime，kCGImagePropertyGIFUnclampedDelayTime值是一样的
          NSDictionary *gifDict = [dict valueForKey:(NSString*)kCGImagePropertyGIFDictionary];
          [delayTimes addObject:[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime]];
  
          totalTime = totalTime + [[gifDict valueForKey:(NSString*)kCGImagePropertyGIFDelayTime] floatValue];
  
          CFRelease((__bridge CFTypeRef)(dict));
      }
  
      if (gifSource) {
          CFRelease(gifSource);
      }
  
      NSMutableArray *times = [NSMutableArray arrayWithCapacity:3];
      CGFloat currentTime = 0;
      NSInteger count = delayTimes.count;
      for (int i = 0; i &lt; count; ++i) {
          [times addObject:[NSNumber numberWithFloat:(currentTime / totalTime)]];
          currentTime += [[delayTimes objectAtIndex:i] floatValue];
      }
  
      NSMutableArray *images = [NSMutableArray arrayWithCapacity:3];
      for (int i = 0; i &lt; count; ++i) {
          [images addObject:[frames objectAtIndex:i]];
      }
  
      animation.keyTimes = times;
      animation.values = images;
      animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
      animation.duration = totalTime;
      animation.repeatCount = HUGE_VALF;
  
      return animation;
  }
  </code></pre>