GIF: Scripting

This section provides a guide to work with the GIF module scripting API. At this stage, it's assumed that you have setup a recorder for the camera you want to record, and created an appropriate clip player to play the recorded clip. If you're not familiar with these concepts, please review the Setup section.

You can access the GIF module API via the Gif class under the EasyMobile namespace. As for Giphy API, use the Giphy class.

Recording

To start recording on the created recorder, use the StartRecording method. You can do this as soon as the game starts; the recorder only stores a few last seconds (specified by the Length parameter in the Recorder inspector) of the recording, and automatically discards the rest.

// Put this on top of the script
using EasyMobile;



// Drag the camera with the Recorder component to this field in the inspector
public Recorder recorder;



// You can start recording as soon as your game starts
// (suppose you have a method named StartGame, which is called when the game starts)
void StartGame()
{
    // Start recording!
    Gif.StartRecording(recorder);    

    // Do other stuff...
}

To stop recording, simply call the StopRecording method, passing the relevant recorder. The method returns an AnimatedClip object, which can be played or exported into a GIF image afterward. To continue the previous example:

// Put this on top of the script
using EasyMobile;



// Drag the camera with the Recorder component to this field in the inspector
public Recorder recorder;

// The recorded clip
AnimatedClip myClip;

// You can start recording as soon as your game starts
// (suppose you have a method named StartGame, which is called when the game starts)
void StartGame()
{
    // Start recording!
    Gif.StartRecording(recorder);    

    // Do other stuff...
}



// A suitable time to stop recording may be when the game ends (the player dies)
// (suppose you have a method named GameOver, called when the game ends)
void GameOver()
{
    // Stop recording
    myClip = Gif.StopRecording(recorder);

    // Do other stuff...
}

Playback

To play a recorded clip using a pre-created clip player, use the PlayClip method. This method receives as argument an IClipPlayer interface, which is implemented by both ClipPlayer and ClipPlayerUI classes, therefore it works with both Clip Player and Clip Player UI object. The second argument is an AnimatedClip object. Other arguments include an optional delay time before the playing starts, and the looping mode. You can pause, resume and stop the player using the PausePlayer, ResumePlayer and StopPlayer methods, respectively.

To continue the previous example:

// Put this on top of the script
using EasyMobile;



// Drag the camera with the Recorder component to this field in the inspector
public Recorder recorder;

// Suppose you've created a ClipPlayerUI object (ClipPlayer will also work)
// Drag the pre-created clip player to this field in the inspector
public ClipPlayerUI clipPlayer;

// The recorded clip
AnimatedClip myClip;

// You can start recording as soon as your game starts
// (suppose you have a method named StartGame, which is called when the game starts)
void StartGame()
{
    // Start recording!
    Gif.StartRecording(recorder);    

    // Do other stuff...
}



// A suitable time to stop recording may be when the game ends (the player dies)
// (suppose you have a method named GameOver, called when the game ends)
void GameOver()
{
    // Stop recording
    myClip = Gif.StopRecording(recorder);

    // Play the recorded clip!
    PlayMyClip();
}



// This method plays the recorded clip on the created player,
// with no delay before playing, and loop indefinitely.
void PlayMyClip()
{
    Gif.PlayClip(clipPlayer, myClip);
}

// This method plays the recorded clip on the created player,
// with a delay of 1 seconds before playing, and loop indefinitely,
// (you can set loop = false to play the clip only once)
void PlayMyClipWithDelay()
{
    Gif.PlayClip(clipPlayer, myClip, 1f, true);
}

// This method pauses the player.
void PausePlayer()
{
    Gif.PausePlayer(clipPlayer);
}

// This method un-pauses the player.
void UnPausePlayer()
{
    Gif.ResumePlayer(clipPlayer);
}

// This method stops the player.
void StopPlayer()
{
    Gif.StopPlayer(clipPlayer);
}

Exporting GIF

To export the recorded clip into a GIF image, use the ExportGif method. In the editor, the exported GIF file will be stored right under the Assets folder; on mobile devices, the storage location is Application.persistentDataPath. You can specify the filename and the quality of the GIF image as well as the priority of the exporting thread. The quality setting accepts values from 1 to 100 (inputs will be clamped to this range). Bigger values will result in better looking GIFs, but will take slightly longer processing time; 80 is generally a good value in terms of time-quality balance. This method has two callbacks: one is called repeatedly during the process and receives the progress value (0 to 1), the other is called when the export completes and receives the file path of the generated image. Though the GIF generation process is done in a separate thread, these callbacks are guaranteed to be called from the main thread, so you can safely access all Unity API from within them.

In the rare case that you want to control the looping mode of the exported GIF (the default is loop indefinitely), use the variant of ExportGif that has a loop parameter (note that some GIF players may ignore this setting):

  • loop < 0: disable looping (play once)
  • loop = 0: loop indefinitely
  • loop > 0: loop a number of times

In the following example, we'll export a GIF image from the recorded clip returned after the recording has stopped.

// Put this on top of the script
using EasyMobile;



// Drag the camera with the Recorder component to this field in the inspector
public Recorder recorder;

// The recorded clip
AnimatedClip myClip;

// You can start recording as soon as your game starts
// (suppose you have a method named StartGame, which is called when the game starts)
void StartGame()
{
    // Start recording!
    Gif.StartRecording(recorder);    

    // Do other stuff...
}



// A suitable time to stop recording can be when the game ends (the player dies)
// (suppose you have a method named GameOver, called when the game ends)
void GameOver()
{
    // Stop recording
    myClip = Gif.StopRecording(recorder);

    // Export GIF image from the resulted clip
    ExportMyGif();
}



// This method exports a GIF image from the recorded clip.
void ExportMyGif()
{
    // Parameter setup
    string filename = "myGif";    // filename, no need the ".gif" extension
    int loop = 0;                 // -1: no loop, 0: loop indefinitely, >0: loop a set number of times
    int quality = 80;             // 80 is a good value in terms of time-quality balance
    System.Threading.ThreadPriority tPriority = System.Threading.ThreadPriority.Normal; // exporting thread priority

    Gif.ExportGif(myClip,
                filename,
                loop,
                quality,
                tPriority,
                OnGifExportProgress,
                OnGifExportCompleted);
}


// This callback is called repeatedly during the GIF exporting process.
// It receives a reference to original clip and a progress value ranging from 0 to 1.
void OnGifExportProgress(AnimatedClip clip, float progress)
{
    Debug.Log(string.Format("Export progress: {0:P0}", progress));
}


// This callback is called once the GIF exporting has completed.
// It receives a reference to the original clip and the filepath of the generated image.
void OnGifExportCompleted(AnimatedClip clip, string path)
{
    Debug.Log("A GIF image has been created at " + path);
}

Disposing of AnimatedClip

Internally, each AnimatedClip object consists of an array of RenderTexture, a "native engine object" type, which is not garbage collected as normal managed types. That means these render textures won't be "destroyed" automatically when their containing clip is garbage collected (the clip object does get collected, but the render textures it references don't, thus creating memory leaks). To take care of this issue, we have the AnimatedClip implement the IDisposable interface and provide the Dispose method to release the render textures, as Unity advised. It's strongly recommended that you call this Dispose method, preferably as soon as you're done with using a clip (e.g. after playing or exporting GIF), to make sure the render textures are properly released and not cause memory issues.

We'll extend the OnGifExportCompleted callback handler of the previous example to dispose the recorded clip as soon as we've generated a GIF image from it.

// This callback is called once the GIF exporting has completed.
// It receives a reference to the original clip and the filepath of the generated image.
void OnGifExportCompleted(AnimatedClip clip, string path)
{
    Debug.Log("A GIF image has been created at " + path);

    // We've done using the clip, dispose it to save memory
    if (clip == myClip)
    {
        myClip.Dispose();
        myClip = null;
    }
}

Since version 2.1.0, we've updated AnimatedClip such that it automatically releases the RenderTexture object once it is collected, thus avoiding memory leaks even if you forget to call Dispose. However, it can take a long time before a clip gets collected by the garbage collector, so it's still a good practice to dispose a clip as soon as you're done using it to avoid wasting memory.

Sharing GIF

Now that a GIF image has been created, you may want to share it (because it's not fun otherwise, is it?). A common approach is to first upload the image to Giphy, a popular GIF hosting site, and then share the returned URL to other social networks like Facebook and Twitter, using Easy Mobile's Native Sharing feature (see the Native Sharing > Scripting section, in particular the ShareURL method).

According to Giphy API documentation, hosted Giphy URLs are supported and play on every major social network.

Upload to Giphy

To upload a GIF image to Giphy, use the Upload method of the Giphy class. You can upload a local image on your device, or an image hosted online, provided that you have its URL. Before doing so, you'll need to prepare the upload content by creating a GiphyUploadParams struct. In this struct you'll specify either the file path of the local image, or the URL of the online image to upload. Note that if both parameters are provided, the local file path will be used over the URL. Within this struct you can also specify other optional parameters such as image tags, the source of the image (e.g. your website), or mark the image as private (only visible by you on Giphy). The Upload method has three callbacks: the first one is called repeatedly during the upload process, receiving a progress value (0 to 1); the second one is called once the upload has completed, receiving the URL of the uploaded image; and the last one will be called if the upload has failed, receiving the error message. All callbacks are called from the main thread.

Giphy Beta and Production Key

The Upload method has two variants: one using Giphy's public beta key, and the other using your own channel username and production API key. The public beta key is meant to be used in development only. According to Giphy Upload API documentation, it is "subject to rate limit constraints", and they "do not encourage live production deployments to use the public key". If you have created a Giphy channel and want to upload GIF images directly to that channel, you'll need to request an Upload Production Key, then provide that key and your channel username to the Upload method.

We'll extend the above example, and modify the OnGifExportCompleted callback handler to upload the GIF image to Giphy once it is created. We'll demonstrate two cases: upload using the public beta key and upload using your own production key.

// This callback is called once the GIF exporting has completed.
// It receives a reference to the original clip and the filepath of the generated image.
void OnGifExportCompleted(AnimatedClip clip, string path)
{
    Debug.Log("A GIF image has been created at " + path);

    // We've done using the clip, dispose it to save memory
    if (clip == myClip)
    {
        myClip.Dispose();
        myClip = null;
    }

    // The GIF image has been created, now we'll upload it to Giphy
    // First prepare the upload content
    var content = new GiphyUploadParams();
    content.localImagePath = path;    // the file path of the generated GIF image
    content.tags = "easy mobile, sglib games, unity";    // optional image tags, comma-delimited
    content.sourcePostUrl = "YOUR_WEBSITE_ADDRESS";    // optional image source, e.g. your website
    content.isHidden = false;    // optional hidden flag, set to true to mark the image as private            

    // Upload the image to Giphy using the public beta key
    UploadToGiphyWithBetaKey(content);
}

// This method uploads a GIF image to Giphy using the public beta key,
// no need to specify any username or API key here.
void UploadToGiphyWithBetaKey(GiphyUploadParams content)
{
    Giphy.Upload(content, OnGiphyUploadProgress, OnGiphyUploadCompleted, OnGiphyUploadFailed);
}

// This method uploads a GIF image to your own Giphy channel,
// using your channel username and production key.
void UploadToGiphyWithProductionKey(GiphyUploadParams content)
{
    Giphy.Upload("YOUR_CHANNEL_USERNAME", "YOUR_PRODUCTION_KEY",
                    content,
                    OnGiphyUploadProgress,
                    OnGiphyUploadCompleted,
                    OnGiphyUploadFailed);
}

// This callback is called repeatedly during the uploading process.
// It receives a progress value ranging from 0 to 1.
void OnGiphyUploadProgress(float progress)
{
    Debug.Log(string.Format("Upload progress: {0:P0}", progress));
}

// This callback is called once the uploading has completed.
// It receives the URL of the uploaded image.
void OnGiphyUploadCompleted(string url)
{
    Debug.Log("The GIF image has been uploaded successfully to Giphy at " + url);
}

// This callback is called if the upload has failed.
// It receives the error message.
void OnGiphyUploadFailed(string error)
{
    Debug.Log("Uploading to Giphy has failed with error: " + error);
}

Display the Giphy Attribution Marks

To request a Production Key, Giphy require you to display the "Powered by Giphy" attribution marks whenever their API is utilized in your app, and provide screenshots of your attribution placement when submitting for the key. To take care of this, we provide the static IsUsingAPI boolean property inside the Giphy class. This property will be true as long as Giphy API is in use, to let you know when to show their attribution marks. You can display the attribution logo using an Image or a Sprite object, then poll this property inside the Update() function, and activate or deactivate the object accordingly.

You can download Giphy's official attribution marks here.

// Drag the object displaying the attribution marks to this field in the inspector
public GameObject attribution;



void Update()
{
    attribution.SetActive(Giphy.IsUsingAPI);
}

Share Giphy URLs

After uploading your GIF image to Giphy and obtain its URL, you can share this URL using the ShareURL method of the MobileNativeShare class. In the example below, we'll modify the OnGiphyUploadCompleted callback handler of the previous example to store the returned URL into a global variable, which can be used for later sharing.

// Global variable to hold the Giphy URL of the uploaded GIF
string giphyURL;



// This callback is called once the uploading has completed.
// It receives the URL of the uploaded image.
void OnGiphyUploadCompleted(string url)
{
    Debug.Log("The GIF image has been uploaded successfully to Giphy at " + url);

    // Store the URL into our global variable
    giphyURL = url;
}



// This method shares the URL using the native sharing utility on iOS and Android
public void ShareGiphyURL()
{
    if (!string.IsNullOrEmpty(giphyURL))
    {
        MobileNativeShare.ShareURL(giphyURL);
    }
}

Decoding GIF

Decoding GIF as AnimatedClip

To decode a GIF image use the DecodeGif method. The GIF image must be stored on the device at a known and accessible file path. You can specify the number of frames to be decoded, if you omit this parameter or use a value smaller than 1 the whole GIF image will be read. You can also specify the priority of the decoding thread. The resulted AnimatedClip will be returned in a callback, which you can play as described in the Playback section.

// The path to the GIF image.
string gifFilepath = "Path/to/your/image.gif";

// Suppose you've created a ClipPlayerUI object (ClipPlayer will also work)
// Drag the pre-created clip player to this field in the inspector
public ClipPlayerUI clipPlayer;


// This method decode the whole GIF image
// at the gifFilepath into an AnimatedClip which can be played with the built-in clip players.
void DecodeGifImage()
{
    Gif.DecodeGif(
        gifFilepath,    // path to the GIF image
        0,              // frames to read: 0 means read the whole GIF   
        System.Threading.ThreadPriority.Normal,    // priority of the decoding thread
        OnGifDecoded    // callback
    );
} 

// This callback is called once the decoding has completed.
void OnGifDecoded(AnimatedClip clip)
{
    if (clip != null)
    {
        // The GIF image has been decoded into an AnimatedClip object!
        // Now you can play it with the built-in clip player...
        Gif.PlayClip(clipPlayer, clip);
    }
    else
    {
        Debug.Log("The decoding failed");
    }    
}

Decoding GIF as Texture Array

You can also decode a GIF image into an array of textures. A typical usage is decoding the first frame of the GIF image into a Texture object and use it as a thumbnail image for the GIF. For this purpose use the DecodeGif overload method that accepts a callback with a Texture array as its parameter. Similar to its other overload, this method also allows you to read the whole GIF or a specified number of frames.

// The path to the GIF image.  
string gifFilepath = "Path/to/your/image.gif";

// This method decode the first frame of the GIF image  
// at the gifFilepath into a Texture array1  
void DecodeGifImage()  
{  
    Gif.DecodeGif(  
        gifFilepath,    // path to the GIF image  
        1,              // only read first frame here; you can put 0 to read the whole GIF
        System.Threading.ThreadPriority.Normal,    // priority of the decoding thread  
        OnGifDecodedAsTextures    // callback  
    );  
}

// This callback is called once the decoding has completed.  
void OnGifDecodedAsTextures(Textures[] textures)  
{  
    if (textures != null && textures.Length > 0)  
    {  
        // The GIF image has been decoded into an array of Texture objects. 
        Texture firstFrame = textures[0];    // grab the first frame of the GIF

        // You can take further action with the decoded textures here...  
    }  
    else  
    {  
        Debug.Log("The decoding failed");  
    }  
}

The GIF decoding APIs currently only support iOS and Android and don't function inside the Unity editor.

results matching ""

    No results matching ""