{"id":21667,"date":"2020-06-07T10:35:00","date_gmt":"2020-06-07T10:35:00","guid":{"rendered":"http:\/\/exoplayer_android_tutorial_easy_video_delivery_and_editing"},"modified":"2025-02-22T14:37:24","modified_gmt":"2025-02-22T22:37:24","slug":"exoplayer_android_tutorial_easy_video_delivery_and_editing","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing","title":{"rendered":"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p><a href=\"https:\/\/github.com\/google\/ExoPlayer\">ExoPlayer<\/a>, a media player for Android, was developed and is being maintained by Google as an alternative for Android\u2019s default <a href=\"https:\/\/developer.android.com\/reference\/android\/media\/MediaPlayer.html\">MediaPlayer<\/a>. Among ExoPlayer\u2019s advantages over MediaPlayer are <a href=\"https:\/\/cloudinary.com\/blog\/optimizing_video_with_cloudinary_and_the_html5_video_player_part_1\">dynamic adaptive streaming<\/a> over HTTP (DASH), smooth streaming, and common <a href=\"https:\/\/cloudinary.com\/glossary\/cenc-encryption\">encryption<\/a>. A major advantage, however, is ExoPlayer\u2019s easy customization.<\/p>\n<p>Given the mobile constraints for resources, such as videos, <a href=\"https:\/\/google.github.io\/ExoPlayer\/\">ExoPlayer<\/a> is an excellent choice for playing videos in Android apps because of its video-buffering capability, which downloads <a href=\"https:\/\/cloudinary.com\/video_api\">videos<\/a> ahead of time for a seamless experience. You can play videos with ExoPlayer from phone storage or from URLs, as described later in this tutorial.<\/p>\n<p>Through an Android example app for ExoPlayer, this tutorial shows you how to leverage Cloudinary, a cloud-based service platform on which you can upload rich media for cloud storage as well as efficiently and effectively manage and transform them, in order to seamlessly display videos on <a href=\"https:\/\/developer.android.com\/guide\/topics\/media\/exoplayer\">ExoPlayer<\/a>.<\/p>\n<p>This is part of a series of articles about <a href=\"https:\/\/cloudinary.com\/guides\/web-performance\/video-optimization-why-you-need-it-and-5-critical-best-practices\">video optimization<\/a>.<\/p>\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Here\u2019s a handy reference guide: <a href=\"https:\/\/www.raywenderlich.com\/5573-media-playback-on-android-with-exoplayer-getting-started\"><em>Media Playback on Android with ExoPlayer: Getting Started<\/em><\/a><\/p><\/div>\n<h2>Uploading and Transforming Videos<\/h2>\n<p>With Cloudinary, you can transform videos by simply tweaking their URLs. Cloudinary then delivers the videos through fast Content Delivery Networks (CDNs) with advanced caching techniques.<\/p>\n<p>Follow the steps below to upload and transform videos in Cloudinary.<\/p>\n<ol>\n<li>\n<p>Replace <code>CLOUDINARY_NAME<\/code> in the <code>AndroidManifest.xml<\/code> file with your cloud name, which is displayed in the console of your Cloudinary account:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta-data<\/span>\n<span class=\"hljs-attr\">android:name<\/span>=<span class=\"hljs-string\">\"CLOUDINARY_URL\"<\/span>\n<span class=\"hljs-attr\">android:value<\/span>=<span class=\"hljs-string\">\"cloudinary:\/\/@CLOUDINARY_NAME\"<\/span>\/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<li>\n<p>Create an application class to initialize Cloudinary once for the app\u2019s entire lifecycle. Given this one-time initialization, the global variables usually reside in this class.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> android.app.Application;\n<span class=\"hljs-keyword\">import<\/span> com.cloudinary.android.MediaManager;\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AppController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Application<\/span> <\/span>{\n    @Override\n    public <span class=\"hljs-keyword\">void<\/span> onCreate() {\n        <span class=\"hljs-keyword\">super<\/span>.onCreate();\n        <span class=\"hljs-comment\">\/\/ Initialize Cloudinary<\/span>\n        MediaManager.init(<span class=\"hljs-keyword\">this<\/span>);\n    }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<li>\n<p>Also in the <code>AndroidManifest.xml<\/code> file, specify <code>AppController<\/code> as the name of the application tag:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">application<\/span>\n    <span class=\"hljs-attr\">android:name<\/span>=<span class=\"hljs-string\">\".AppController\"<\/span> &gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Your uploads can be unsigned or signed. Signed uploads, which offer certain benefits, require an API key, which is not recommended for Android clients because it can be easily decompiled. Hence, opt for unsigned upload here.<\/p><\/div>\n<\/li>\n<li>\n<p>Enable unsigned uploads in your console: click the <strong>Settings<\/strong> icon on your dashboard and then the <strong>Upload<\/strong> tab, scroll down to the <strong>Upload Presets<\/strong> section, and enable unsigned uploading. Cloudinary then generates a new preset with a random string as its name.<\/p>\n<\/li>\n<li>\n<p>Upload to Cloudinary by calling this function:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">MediaManager.get()\n    .upload(videoUri)\n    .unsigned(<span class=\"hljs-string\">\"YOUR_PRESET\"<\/span>)\n    .option(<span class=\"hljs-string\">\"resource_type\"<\/span>, <span class=\"hljs-string\">\"video\"<\/span>)\n    .callback(<span class=\"hljs-keyword\">new<\/span> UploadCallback() {\n        @Override\n        public <span class=\"hljs-keyword\">void<\/span> onStart(<span class=\"hljs-built_in\">String<\/span> requestId) {\n\n        }\n\n        @Override\n        public <span class=\"hljs-keyword\">void<\/span> onProgress(<span class=\"hljs-built_in\">String<\/span> requestId, long bytes, long totalBytes) {\n\n        }\n\n        @Override\n        public <span class=\"hljs-keyword\">void<\/span> onSuccess(<span class=\"hljs-built_in\">String<\/span> requestId, <span class=\"hljs-built_in\">Map<\/span> resultData) {\n\n        }\n\n        @Override\n        public <span class=\"hljs-keyword\">void<\/span> onError(<span class=\"hljs-built_in\">String<\/span> requestId, ErrorInfo error) {\n\n        }\n\n        @Override\n        public <span class=\"hljs-keyword\">void<\/span> onReschedule(<span class=\"hljs-built_in\">String<\/span> requestId, ErrorInfo error) {\n\n        }\n    }).dispatch();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<\/ol>\n<p><code>videoUri<\/code> is of type <code>Uri<\/code>, which represents the Uniform Resource Identifier (URI) of the video that resides in your phone, e.g., <code>content:\/\/media\/external\/video\/media\/3495<\/code>.<\/p>\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Be sure to replace <code>YOUR_PRESET<\/code> with the string generated by Cloudinary after you\u2019ve enabled unsigned upload.<\/p><\/div>\n<p>On a successful upload, Cloudinary calls the <code>onSuccess<\/code> method, which contains the details of your upload, such as <code>URL<\/code> (the URL) and <code>public_url<\/code> (the public URL), which is the unique name of the video as stored in Cloudinary.<\/p>\n<p>Next, transform the video in the method below by referring to <code>public_url<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-built_in\">String<\/span> publicUrl = = (<span class=\"hljs-built_in\">String<\/span>) resultData.get(<span class=\"hljs-string\">\"public_url\"<\/span>);\n<span class=\"hljs-built_in\">String<\/span> transformedUrl = MediaManager.get().url()\n        .transformation(<span class=\"hljs-keyword\">new<\/span> Transformation()\n                .effect(<span class=\"hljs-string\">\"fade:2000\"<\/span>).chain()\n                .effect(<span class=\"hljs-string\">\"fade:-3000\"<\/span>).chain()\n                .effect(<span class=\"hljs-string\">\"saturation:-50\"<\/span>)\n        .resourceType(<span class=\"hljs-string\">\"video\"<\/span>).generate(publicUrl+<span class=\"hljs-string\">\".mp4\"<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You\u2019ve now added three effects to the video:<\/p>\n<ul>\n<li>A two-second fade-in. Fade-ins usually have a positive value.<\/li>\n<li>A three-second fade-out. Fade-outs usually have a negative value.<\/li>\n<li>A drop in the saturation. A negative saturation value results in a faded look of the video.<\/li>\n<\/ul>\n<p>A printout of <code>transformedUrl<\/code> reads like this: <code>https:\/\/res.cloudinary.com\/{CLOUD_NAME}\/video\/upload\/e_fade:2000\/e_fade:-3000\/e_saturation:-50\/{publicUrl}.mp4<\/code><\/p>\n<p>For details on Cloudinary\u2019s many transformation capabilities, see this <a href=\"https:\/\/cloudinary.com\/blog\/introducing_the_complete_video_solution_for_web_and_mobile_developers#real_time_video_transcoding_manipulation_and_streaming\">section<\/a> in a Cloudinary post. To learn about video transformations on Cloudinary, see the <a href=\"https:\/\/cloudinary.com\/documentation\/video_manipulation_and_delivery#video_effects\">related documentation<\/a>.<\/p>\n<h2>Setting Up ExoPlayer for Android Apps<\/h2>\n<p>It is assumed that you have developed an Android app, hence this section does not address that process. Follow the steps below to set up ExoPlayer for your app:<\/p>\n<ol>\n<li>\n<p>Add the Gradle dependency to your <code>build.gradle<\/code> file to make use of the ExoPlayer library:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">implementation <span class=\"hljs-string\">'com.google.android.exoplayer:exoplayer:2.6.0'<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>{note}\nIf your Gradle version is below 3.0, replace <code>implementation<\/code> in the above snippet with <code>compile<\/code>.\n{\/note}<\/p>\n<\/li>\n<li>\n<p>Sync your <code>gradle<\/code> file to ensure a download of the dependency for the project.<\/p>\n<\/li>\n<li>\n<p>Add <code>SimpleExoPlayerView<\/code> to your <code>Activity<\/code> layout:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-meta\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">LinearLayout<\/span> <span class=\"hljs-attr\">xmlns:android<\/span>=<span class=\"hljs-string\">\"https:\/\/schemas.android.com\/apk\/res\/android\"<\/span>\n    <span class=\"hljs-attr\">xmlns:tools<\/span>=<span class=\"hljs-string\">\"https:\/\/schemas.android.com\/tools\"<\/span>\n    <span class=\"hljs-attr\">android:layout_width<\/span>=<span class=\"hljs-string\">\"match_parent\"<\/span>\n    <span class=\"hljs-attr\">android:layout_height<\/span>=<span class=\"hljs-string\">\"180dp\"<\/span>\n    <span class=\"hljs-attr\">android:layout_margin<\/span>=<span class=\"hljs-string\">\"16dp\"<\/span>&gt;<\/span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">com.google.android.exoplayer2.ui.SimpleExoPlayerView<\/span>\n        <span class=\"hljs-attr\">android:id<\/span>=<span class=\"hljs-string\">\"@+id\/exoplayer\"<\/span>\n        <span class=\"hljs-attr\">android:layout_width<\/span>=<span class=\"hljs-string\">\"match_parent\"<\/span>\n        <span class=\"hljs-attr\">android:layout_height<\/span>=<span class=\"hljs-string\">\"wrap_content\"<\/span>\/&gt;<\/span>        \n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<li>\n<p>In the <code>onStart<\/code> method in the corresponding <code>Activity<\/code> class, initialize <code>SimpleExoPlayerView<\/code> and set up <code>SimpleExoPlayer<\/code> by calling the <code>initializePlayer<\/code> method:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-keyword\">@Override<\/span>\nprotected void onStart() {\n    <span class=\"hljs-selector-tag\">super<\/span><span class=\"hljs-selector-class\">.onStart<\/span>();\n    <span class=\"hljs-selector-tag\">initializePlayer<\/span>();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><code>initializePlayer<\/code> applies the default configurations for a seamless display of videos in ExoPlayer:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">private <span class=\"hljs-keyword\">void<\/span> initializePlayer(){\n<span class=\"hljs-comment\">\/\/ Create a default TrackSelector<\/span>\nBandwidthMeter bandwidthMeter = <span class=\"hljs-keyword\">new<\/span> DefaultBandwidthMeter();\nTrackSelection.Factory videoTrackSelectionFactory =\n        <span class=\"hljs-keyword\">new<\/span> AdaptiveTrackSelection.Factory(bandwidthMeter);\nTrackSelector trackSelector =\n        <span class=\"hljs-keyword\">new<\/span> DefaultTrackSelector(videoTrackSelectionFactory);\n\n<span class=\"hljs-comment\">\/\/Initialize the player<\/span>\nplayer = ExoPlayerFactory.newSimpleInstance(<span class=\"hljs-keyword\">this<\/span>, trackSelector);\n\n<span class=\"hljs-comment\">\/\/Initialize simpleExoPlayerView<\/span>\nSimpleExoPlayerView simpleExoPlayerView = findViewById(R.id.exoplayer);\nsimpleExoPlayerView.setPlayer(player);\n\n<span class=\"hljs-comment\">\/\/ Produces DataSource instances through which media data is loaded.<\/span>\nDataSource.Factory dataSourceFactory =\n        <span class=\"hljs-keyword\">new<\/span> DefaultDataSourceFactory(<span class=\"hljs-keyword\">this<\/span>, Util.getUserAgent(<span class=\"hljs-keyword\">this<\/span>, <span class=\"hljs-string\">\"CloudinaryExoplayer\"<\/span>));\n\n<span class=\"hljs-comment\">\/\/ Produces Extractor instances for parsing the media data.<\/span>\nExtractorsFactory extractorsFactory = <span class=\"hljs-keyword\">new<\/span> DefaultExtractorsFactory();\n\n<span class=\"hljs-comment\">\/\/ This is the MediaSource representing the media to be played.<\/span>\nUri videoUri = Uri.parse(<span class=\"hljs-string\">\"any Cloudinary URL\"<\/span>);\nMediaSource videoSource = <span class=\"hljs-keyword\">new<\/span> ExtractorMediaSource(videoUri,\n        dataSourceFactory, extractorsFactory, <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-literal\">null<\/span>);\n\n<span class=\"hljs-comment\">\/\/ Prepare the player with the source.<\/span>\nplayer.prepare(videoSource);\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<\/ol>\n<p>The above snippet performs these tasks:<\/p>\n<ul>\n<li>Initializes the <code>SimpleExoPlayer<\/code> instance with the default configurations.<\/li>\n<li>Creates and initializes an instance of <code>SimpleExoPlayerView<\/code> and assigns your existing player instance to it.<\/li>\n<li>Generates the media source with <code>videoUri<\/code> parsed from a video URL (<code>URL<\/code>) from Cloudinary.<\/li>\n<\/ul>\n<p>Now prepare the player with the video source, after which the video is ready for display. You now have a basic implementation of ExoPlayer.<\/p>\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>The <code>SimpleExoPlayer<\/code> instance is a class variable here, accessible to all the methods in the class.<\/p><\/div>\n<p><strong>5.<\/strong> To save resources, add the code below to release the player when it\u2019s not in use:<\/p>\n<pre><code>```java\n@Override\npublic void onPause() {\n    super.onPause();\n    if (player!=null) {\n        player.release();\n        player = null;\n    }\n}\n```\n<\/code><\/pre>\n<p>Finally, add the permissions for the app to access the internet in the <code>AndroidManifest.xml<\/code> file :<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">uses-permission<\/span> <span class=\"hljs-attr\">android:name<\/span>=<span class=\"hljs-string\">\"android.permission.INTERNET\"<\/span>\/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The app looks like this if you play the transformed URL with ExoPlayer.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-res.cloudinary.com\/image\/upload\/w_300,c_fill,f_auto,q_auto,dpr_2.0\/ExoPlayer.png\" alt=\"ExoPlayer\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"300\" height=\"533.5\"\/><\/p>\n<h2>Exploring Cloudinary Further<\/h2>\n<p>You can integrate <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">Cloudinary<\/a>, which offers robust capabilities for video management, with libraries like ExoPlayer to display videos on Android apps. It\u2019s an intuitive, easy process. For details on optimizing videos with Cloudinary, see this <a href=\"https:\/\/cloudinary.com\/blog\/optimizing_video_with_cloudinary_and_the_html5_video_player_part_1\">post<\/a>. Also see the Cloudinary <a href=\"https:\/\/cloudinary.com\/documentation\">documentation<\/a> for other media-management features.<\/p>\n<hr \/>\n<h2>Want to Learn More About Video Optimization?<\/h2>\n<ul>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/optimizing_video_with_cloudinary_and_the_html5_video_player_part_1\">Optimizing Video with Cloudinary and the HTML5 Video Player<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\">ExoPlayer Android Tutorial: Easy Video Delivery and Editing<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/how_to_generate_waveform_images_from_audio_files\">How to Generate Waveform Images From Audio Files<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/with_automatic_video_subtitles_silence_speaks_volumes\">Auto Generate Subtitles Based on Video Transcript<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/auto_generate_video_previews_with_great_results_every_time\">Automated Generation of Intelligent Video Previews on Cloudinary\u2019s Dynamic Video Platform<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/converting_android_videos_to_animated_gif_images_with_cloudinary_a_tutorial\">Converting Android Videos to Animated GIF Images With Cloudinary: A Tutorial<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/guides\/marketing-videos\/tips-for-retaining-audience-through-engaging-videos\">Tips for Retaining Audience Through Engaging Videos<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/guides\/marketing-videos\/product-videos-101-what-makes-them-great\">Product Videos 101: What Makes Them Great?<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":21668,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[333,119,165,202,305],"class_list":["post-21667","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-android","tag-file-upload","tag-image-transformation","tag-mobile","tag-video-api"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>ExoPlayer Android Tutorial: Easy Video Delivery and Editing<\/title>\n<meta name=\"description\" content=\"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial\" \/>\n<meta property=\"og:description\" content=\"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2020-06-07T10:35:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-22T22:37:24+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1540\" \/>\n\t<meta property=\"og:image:height\" content=\"847\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial\",\"datePublished\":\"2020-06-07T10:35:00+00:00\",\"dateModified\":\"2025-02-22T22:37:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\"},\"wordCount\":11,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA\",\"keywords\":[\"Android\",\"File-upload\",\"Image Transformation\",\"Mobile\",\"Video API\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2020\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\",\"url\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\",\"name\":\"ExoPlayer Android Tutorial: Easy Video Delivery and Editing\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA\",\"datePublished\":\"2020-06-07T10:35:00+00:00\",\"dateModified\":\"2025-02-22T22:37:24+00:00\",\"description\":\"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA\",\"width\":1540,\"height\":847},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"ExoPlayer Android Tutorial: Easy Video Delivery and Editing","description":"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing","og_locale":"en_US","og_type":"article","og_title":"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial","og_description":"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.","og_url":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing","og_site_name":"Cloudinary Blog","article_published_time":"2020-06-07T10:35:00+00:00","article_modified_time":"2025-02-22T22:37:24+00:00","og_image":[{"width":1540,"height":847,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing"},"author":{"name":"","@id":""},"headline":"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial","datePublished":"2020-06-07T10:35:00+00:00","dateModified":"2025-02-22T22:37:24+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing"},"wordCount":11,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA","keywords":["Android","File-upload","Image Transformation","Mobile","Video API"],"inLanguage":"en-US","copyrightYear":"2020","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing","url":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing","name":"ExoPlayer Android Tutorial: Easy Video Delivery and Editing","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA","datePublished":"2020-06-07T10:35:00+00:00","dateModified":"2025-02-22T22:37:24+00:00","description":"Learn how to transform and deliver videos with Android\u2019s ExoPlayer and Cloudinary.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA","width":1540,"height":847},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/exoplayer_android_tutorial_easy_video_delivery_and_editing#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Easy Video Editing and Delivery for the Android ExoPlayer: A Tutorial"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649727469\/Web_Assets\/blog\/ExoPlayer_Video_Delivery_Transformation_v1a_1\/ExoPlayer_Video_Delivery_Transformation_v1a_1.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21667","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=21667"}],"version-history":[{"count":7,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21667\/revisions"}],"predecessor-version":[{"id":36954,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21667\/revisions\/36954"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/21668"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=21667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=21667"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=21667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}