워드프레스 애니메이션 gif 파일을 mp4로 변환하여 출력하기

최근에 애니메이션 gif 파일을 mp4로 변경해서 출력하는 게 유행(?)이라고 할까? 포스팅 때 애니메이션 gif 파일을 첨부하지는 않기 때문에 굳이 관심을 가져야 할 이유가 없었는데.. 오늘은 왠지 한번 해볼까? 하는 생각이 들어서 기본 기능만 작동하도록 플러그인을 만들어 봤다. 개발 환경은 아래와 같다.

  • WordPress : 5.5
  • PHP : 7.4.9
  • ffmpeg : 4.2.4

gif 파일을 mp4 및 webm 파일로 변경하는 코드는 아래와 같다. 이 파일을 functions.php 파일로 저장한다.

<?php

// https://www.php.net/manual/en/function.imagecreatefromgif.php#104473
function isAnimatedGIF($gif) {
    if(!($fh = @fopen($gif, 'rb')))
        return false;

    $count = 0;
    //an animated gif contains multiple "frames", with each frame having a
    //header made up of:
    // * a static 4-byte sequence (\x00\x21\xF9\x04)
    // * 4 variable bytes
    // * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)

    // We read through the file til we reach the end of the file, or we've found
    // at least 2 frame headers
    while(!feof($fh) && $count < 2) {
        $chunk = fread($fh, 1024 * 100); //read 100kb at a time
        $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
    }

    fclose($fh);

    return $count > 1;
}

function convert_gif2mp4($content)
{
    $images = array();

    $upload_info = wp_upload_dir();
    $upload_dir = $upload_info['basedir'];
    $upload_url = $upload_info['baseurl'];

    $pattern = '#<img[^>]*src=[\'"]?(.+\.gif)[\'"]?[^>]*>#i';

    preg_match_all($pattern, $content, $matches);

    $count = count($matches[1]);
    $urls  = array();

    if ( $count > 0 ) {
        for ( $i=0; $i<$count; $i++ ) {
            $url  = $matches[1][$i];
            $url2 = preg_replace('#^https?:#i', '', $url);

            if(in_array($url2, $urls))
                continue;

            $urls[] = $url;

            // 로컬 파일인지 체크
            if(strpos( $url, $upload_url ) === false)
                continue;

            // 이미지 경로 설정
            $rel_path = str_replace( $upload_url, '', $url);
            $img_file = $upload_dir . $rel_path;

            // gif 파일인지 체크
            if( !is_file($img_file))
                continue;

            $size = @getimagesize($img_file);
            if($size[2] != 1)
                continue;

            // 애니메이션 gif 체크
            if (!isAnimatedGIF($img_file))
                continue;

            // mp4 파일 생성
            $pinfo = pathinfo($img_file);

            $mp4 = $pinfo['dirname'].'/'.$pinfo['filename'].'.mp4';
            $webm = $pinfo['dirname'].'/'.$pinfo['filename'].'.webm';
            $poster = $pinfo['dirname'].'/poster_'.$pinfo['filename'].'.jpg';

            if (is_writable($pinfo['dirname']) && !is_file($mp4)) {
                try {
                    $poster = $pinfo['dirname'].'/poster_'.$pinfo['filename'].'.jpg';
                    $image = @imagecreatefromgif($img_file);

                    imagejpeg($image, $poster, 90);

                    @exec('ffmpeg -i '.escapeshellcmd(preg_replace('/[^0-9A-Za-z_\-\.\\\\\/]/i', '', $img_file)).' -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -pix_fmt yuv420p -movflags +faststart  '.escapeshellcmd(preg_replace('/[^0-9A-Za-z_\-\.\\\\\/]/i', '', $mp4)));

                    @exec('ffmpeg -i '.escapeshellcmd(preg_replace('/[^0-9A-Za-z_\-\.\\\\\/]/i', '', $img_file)).' -c vp9 -b:v 0 -crf 41  '.escapeshellcmd(preg_replace('/[^0-9A-Za-z_\-\.\\\\\/]/i', '', $webm)));
                } catch(Exception $e) {
                    continue;
                }
            }

            if (is_file($mp4)) {
                $video = '<video poster="'.str_replace($upload_dir, $upload_url, $poster).'" autoplay="autoplay" loop="loop" preload="auto" playsinline webkit-playsinline muted>';
                if (is_file($webm))
                    $video .= '<source src="'.str_replace($upload_dir, $upload_url, $webm).'" type="video/webm">';
                $video .= '<source src="'.str_replace($upload_dir, $upload_url, $mp4).'" type="video/mp4">';
                $video .= '</video>';

                $content = str_replace($matches[0][$i], $video, $content);
            }
        }
    }

    return $content;
}

워드프레스 플러그인 적용을 위한 gif2mp4.php 파일은 아래와 같다.

<?php

/**
 * @package Convert GIF to MP4 in Post
 * @version 1.0.0
 */
/*
Plugin Name: Convert GIF to MP4 in Post
Plugin URI: https://ncube.net/
Description: This plugin animated gif to mp4 using ffmpeg in post.
Author: chicpro
Version: 1.0.0
Author URI: https://ncube.net/
*/

require ( plugin_dir_path( __FILE__ ) . '/functions.php' );

add_filter ('the_content', 'convert_gif2mp4', 100);

위 두개의 파일을 wp-content > plugins 폴더 안에 gif2mp4 와같은 폴더를 생성한 후 그 안에 넣어준다. 그런 다음 워드프레스 관리자 페이지에서 플러그인을 활성해 주면 된다. 포스트 내용에 gif 파일이 출력되는 코드에 따라 $pattern = '#<img[^>]*src=[\'"]?(.+\.gif)[\'"]?[^>]*>#i'; 이 부분을 적당히 수정해야 할 수도 있다. 전체 코드는 github 에서 다운로드 할 수 있다.

github repo : https://github.com/chicpro/wordpress-gif2mp4

8 Replies to “워드프레스 애니메이션 gif 파일을 mp4로 변환하여 출력하기”

    1. 아마 기기에 따라서 선택되어 재생이 될 것으로 생각됩니다.
      외국 쪽 자료에서는 mp4와 webm을 동시에 지원하는 게 많습니다.

  1. 참고해서 그누보드 5.4용 플러그인을 수정해보았습니다.

    그런데 출력할 때

    if (is_file($webm))

    이 부분은 그누보드에서 어떻게 처리해야될지 모르겠습니다.

    일단 그냥 if 부분없이 다 출력되도록 했거든요.

    그러니 정상적으로 소스에 webm과 mp4가 둘다 들어갔습니다.

    if webm 부분은 왜 넣었을까요?

    gif 중에 webm 생성이 안되는 파일이 있는건가요?

    1. 음.. 저는 거의 습관적으로 파일 유무를 체크하는 것 같습니다.
      테스트 중 webm 파일이 생성되지 않은 경우는 없지만..
      오류라는 건 언제 발생할지 모르니까요..

  2. 일단 webm과 mp4를 동시에 출력이 되게 설정했습니다.

    플러그인을 수정해서 그누보드 플러그인에 올려도 되겠죠? ^^;;;

    감사합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.