Wednesday 24 October 2018

How to add filters (transition) between two videos using php-ffmpeg?

How to add filters (transition) between two videos using php-ffmpeg?

Question: What are the different filters available?
  1. alphaextract: Extract the alpha component from the input as a grayscale video.
  2. alphamerge: Add or replace the alpha component of the primary input with the grayscale value of a second input
    movie=in_alpha.mkv [alpha]; [in][alpha] alphamerge [out]
  3. amplify: Amplify differences between current pixel and pixels of adjacent frames in same pixel location.
  4. atadenoise: Adaptive Temporal Averaging Denoiser to the video input.
  5. avgblur: Apply average blur filter.
  6. bbox: Compute the bounding box for the non-black pixels in the input frame luminance plane.
  7. bitplanenoise: Show and measure bit plane noise.
  8. blackdetect: Detect video intervals that are (almost) completely black.
    blackdetect=d=2:pix_th=0.00



Question: Give few examples?
Apply transition from bottom layer to top layer in first 10 seconds
blend=all_expr='A*(if(gte(T,10),1,T/10))+B*(1-(if(gte(T,10),1,T/10)))'


Apply uncover left effect:
blend=all_expr='if(gte(N*SW+X,W),A,B)'


Apply uncover down effect:
blend=all_expr='if(gte(Y-N*SH,0),A,B)'


Display differences between the current and the previous frame:
tblend=all_mode=grainextract





Question: How to add video filter (transition) on videos with PHP-FFMPEG?
  1. You must have installed PHP-FFMPEG
  2. php composer.phar require php-ffmpeg/php-ffmpeg
    If not, you can check https://www.web-technology-experts-notes.in/2017/10/how-to-install-ffmpeg-in-wamp-server-in-windows7.html

  • Open File Path: \vendor\php-ffmpeg\php-ffmpeg\src\FFMpeg\Media\Concat.php
  • Add following function in Concat.php
     public function saveFromDifferentCodecsTransition(FormatInterface $format, $outputPathfile,$transition=array())
        {
            /**
             * @see https://ffmpeg.org/ffmpeg-formats.html#concat
             * @see https://trac.ffmpeg.org/wiki/Concatenate
             */
    
            // Check the validity of the parameter
            if(!is_array($this->sources) || (count($this->sources) == 0)) {
                throw new InvalidArgumentException('The list of videos is not a valid array.');
            }
    
            // Create the commands variable
            $commands = array();
    
            // Prepare the parameters
            $nbSources = 0;
            $files = array();
    
            // For each source, check if this is a legit file
            // and prepare the parameters
            foreach ($this->sources as $videoPath) {
                $files[] = '-i';
                $files[] = $videoPath;
                $nbSources++;
            }
    
            $commands = array_merge($commands, $files);
    
            // Set the parameters of the request
            $commands[] = '-filter_complex';
    
            $complex_filter = '';
            //$transition =array('transition_effect'=>'fade_out','transition_length'=>1.2);
            
            //No transistion
            if(empty($transition) || @$transition['transition_effect']=='none' || $nbSources==1){
                for($i=0; $i<$nbSources; $i++) {
                    $complex_filter .= '['.$i.':v:0] ['.$i.':a:0] ';
                }   
                
            }else{ //Transition with Fade in/Fade out
                if($transition['transition_effect']=='fade_in'){
                    for($i=0; $i<$nbSources; $i++) {
                        if($i == 0){
                            $complex_filter .='['.$i.':v]setpts=PTS-STARTPTS[v'.$i.'];';            
                        }else{
    
            
                            $complex_filter .='['.$i.':v]fade=type=in:duration='.$transition['transition_length'].',setpts=PTS-STARTPTS[v'.$i.'];';            
                        }
                    }
                }else if($transition['transition_effect']=='fade_out'){
                    for($i=0; $i<$nbSources; $i++) {
                        if($i == ($nbSources-1)){
                            $complex_filter .='['.$i.':v]setpts=PTS-STARTPTS[v'.$i.'];';            
                        }else{
                            $startTime=0;
                            /* Get duration  of video*/
                               try{
                                    $sourceFile = $this->sources[$i];
                                    $ffprobe    = \FFMpeg\FFProbe::create();
                                    $duration=
                                            $ffprobe->streams($sourceFile)
                                            ->videos()                   
                                            ->first()                  
                                            ->get('duration');  
                                    $startTime=$duration-$transition['transition_length'];
                               }catch(Exception $e){
                                   $transition['transition_length']=0;
                               }
                            /* Get duration  of video*/
                            $complex_filter .='['.$i.':v]fade=type=out:start_time='.$startTime.',setpts=PTS-STARTPTS[v'.$i.'];';            
                        }
                    }
                }
                    for($i=0; $i<$nbSources; $i++) {
                        $complex_filter .= '[v'.$i.']['.$i.':a]';
                    }             
                
            }
            
            $complex_filter .= 'concat=n='.$nbSources.':v=1:a=1 [v] [a]';
    
            $commands[] = $complex_filter;
            $commands[] = '-map';
            $commands[] = '[v]';
            $commands[] = '-map';
            $commands[] = '[a]';
    
            // Prepare the filters
            $filters = clone $this->filters;
            $filters->add(new SimpleFilter($format->getExtraParams(), 10));
    
            if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
                $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
            }
            if ($format instanceof VideoInterface) {
                if (null !== $format->getVideoCodec()) {
                    $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
                }
            }
            if ($format instanceof AudioInterface) {
                if (null !== $format->getAudioCodec()) {
                    $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
                }
            }
    
            // Add the filters
            foreach ($this->filters as $filter) {
                $commands = array_merge($commands, $filter->apply($this));
            }
    
            if ($format instanceof AudioInterface) {
                if (null !== $format->getAudioKiloBitrate()) {
                    $commands[] = '-b:a';
                    $commands[] = $format->getAudioKiloBitrate() . 'k';
                }
                if (null !== $format->getAudioChannels()) {
                    $commands[] = '-ac';
                    $commands[] = $format->getAudioChannels();
                }
            }
    
            // If the user passed some additional parameters
            if ($format instanceof VideoInterface) {
                if (null !== $format->getAdditionalParameters()) {
                    foreach ($format->getAdditionalParameters() as $additionalParameter) {
                        $commands[] = $additionalParameter;
                    }
                }
            }
    
            // Set the output file in the command
            $commands[] = $outputPathfile;
    
            $failure = null;
    
            try {
                $this->driver->command($commands);
            } catch (ExecutionFailureException $e) {
                throw new RuntimeException('Encoding failed', $e->getCode(), $e);
            }
    
            return $this;
        }



  • Question: How to call above function (used for scale in/scale out) with number of seconds?
    require_once 'ffmpeglib/vendor/autoload.php';
    $ffmpeg = FFMpeg\FFMpeg::create();
    $format = new FFMpeg\Format\Video\X264();
    $format->setAudioCodec("aac");  
    $newFileName='output_video.mp4';
    $captionStaticFilePath=$_SERVER['DOCUMENT_ROOT'].'/myvideos/';
    $videoFiles=array($captionStaticFilePath.'myvideo.mp4',$captionStaticFilePath.'myvideo1.mp4',$captionStaticFilePath.'myvideo2.mp4');
    
    /////////////// Debug ////////////////////////////
    $ffmpeg->getFFMpegDriver()->listen(new \Alchemy\BinaryDriver\Listeners\DebugListener());
    $ffmpeg->getFFMpegDriver()->on('debug', function ($message) {
       echo $message."
    ";
    });
    /////////////// Debug ////////////////////////////    
    $transition=array(
        'transition_effect'=>'fade_in', //fade_in,fade_out, black
        'transition_length'=>4 //number of seconds
    ); 
    try{
    $video = $ffmpeg->open($videoFiles[0])                        
        ->concat($videoFiles)
        ->saveFromDifferentCodecsTransition(new FFMpeg\Format\Video\X264,$captionStaticFilePath.$newFileName,$transition); 
    }catch(Exception $e){
    echo $e->getMessage();
     } 
    



    How to add formated text on video using php-ffmpeg?

    How to add formated text on video using php-ffmpeg?

    Question: What is drawtext on video?
    Adding the single line text/multiple line text over the video.



    Question: Can we change the font-size of text?
    Yes, we can change the color, size, font family etc of text in videos.



    Question: How to add text on videos?
    ffmpeg -i myvideo.mp4 -vf drawtext="fontfile=OpenSans-Bold.ttf: text=this is text on videos: fontcolor=white: fontsize=120: box=1: boxcolor=black@0.5: boxborderw=5: x=(w-text_w)/2: y=(h-text_h)/2" -codec:a copy output_video.mp4
    

    Note: OpenSans-Bold.ttf file must available (with parallel to video i.e myvideo.mp4)



    Question: How to multiple line text on videos?
    ffmpeg -i myvideo.mp4 -vf "[in]drawtext=fontfile=OpenSans-Bold.ttf: text=Arun kumar: fontcolor=white: fontsize=20: box=1: boxcolor=black@0.5: boxborderw=5: x=100: y=100 drawtext=fontfile=OpenSans-Bold.ttf: text='Kuldeep ': fontcolor=white: fontsize=20: box=1: boxcolor=black@0.5: boxborderw=5: x=100: y=400[output]" -codec:a copy output_video.mp4
    

    Note: OpenSans-Bold.ttf file must available (with parallel to video i.e myvideo.mp4)




    Question: How to add text in video with PHP?
    1. You must have installed PHP-FFMPEG
      php composer.phar require php-ffmpeg/php-ffmpeg
      If not, you can check https://www.web-technology-experts-notes.in/2017/10/how-to-install-ffmpeg-in-wamp-server-in-windows7.html
    2. To achieve this, you need to use the ffmpeg filters.
    3. require_once 'ffmpeglib/vendor/autoload.php';
      $ffmpeg = FFMpeg\FFMpeg::create();
      $format = new FFMpeg\Format\Video\X264();
       $format->setAudioCodec("aac");  
      $videoFile='myvideo.mp4';
      $captionStaticFilePath='';//directory path
      $captionStaticFilePath=$_SERVER['DOCUMENT_ROOT'].'/video/';
      
      /////////////// Debug ////////////////////////////
      /*$ffmpeg->getFFMpegDriver()->listen(new \Alchemy\BinaryDriver\Listeners\DebugListener());
      $ffmpeg->getFFMpegDriver()->on('debug', function ($message) {
          echo $message;
      });*/
      /////////////// Debug ////////////////////////////                
      
      /* Filter the text correctly*/ 
      $captionJSON=json_decode('["text=This is caption text: fontfile=OpenSans-Regular.ttf: x=384: y=10: fontcolor=#ffffff: fontsize=24px","text=This is clip 2 - Samik das: fontfile=OpenSans-Bold.ttf: x=404.08331298828125: y=34.91668701171875: fontcolor=#ffffff: fontsize=24px: fonttype=bold"]');
      $captionArray=array();
      if(!empty($captionJSON) && !empty($captionJSON)){
          foreach($captionJSON as $caption){
              //String to array
              $caption=str_replace(':', '&', $caption);
              parse_str($caption, $output);
              //pr($output);die;
      
              unset($output['fonttype']);
              $output['fontsize']=str_replace('px','',$output['fontsize']);
              $output['fontfile']=$captionStaticFilePath.$output['fontfile'];
      
              $output['x'] = (int)$output['x'];
              $output['y'] = (int)$output['y'];
      
              //Array to string
              $outputStr='';
              $counter=0;
              foreach($output as $i=>$v){
                  if($counter){
                      $outputStr.= ': ';
                  }
                  $outputStr.= $i.'='.$v;
                  $counter++;
              }
              //Update to array                            
              $captionArray[]=$outputStr;
          }
      }
      /* Filter the text correctly*/ 
      
      
      try{
          $video = $ffmpeg->open($captionStaticFilePath.$videoFile);
          $command='';
          if(!empty($captionArray)){
              $command='[in]';
              foreach($captionArray as $index=>$captionComand){
                  if($index>0){
                      $command= $command.',';
                  }
                  $command= $command.'drawtext='.$captionComand;
              }
              $command.='[out]';
      
      
          }
      
          //nOW execute the command
          $video->filters()->custom($command);
          $captionFile='caption_'.$videoFile;
          //Save the video with caption
          $video->save($format, $captionStaticFilePath.$captionFile);
          die('done');
      }catch(Exception $e){
          echo $e->getMessage();die;
      }



    Question: How to add debugging in php-ffmpeg?
    Just add below code after the initialize the ffmpeg object.

    $ffmpeg->getFFMpegDriver()->listen(new \Alchemy\BinaryDriver\Listeners\DebugListener());
    $ffmpeg->getFFMpegDriver()->on('debug', function ($message) {
        echo $message;
    });



    Question: Share Few useful urls for drawtext?
    https://www.ffmpeg.org/ffmpeg-all.html#fade
    https://ffmpeg.org/ffmpeg-filters.html