<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pradip Shrestha</title>
    <description>The latest articles on DEV Community by Pradip Shrestha (@pradip_shrestha).</description>
    <link>https://dev.to/pradip_shrestha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3709441%2F617b3188-aa27-4ce7-b5b0-6a2376ea93b3.jpg</url>
      <title>DEV Community: Pradip Shrestha</title>
      <link>https://dev.to/pradip_shrestha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pradip_shrestha"/>
    <language>en</language>
    <item>
      <title>Why Manual YUV Video Encoding Fails Across Android Devices (and How to Fix It)</title>
      <dc:creator>Pradip Shrestha</dc:creator>
      <pubDate>Tue, 13 Jan 2026 17:00:52 +0000</pubDate>
      <link>https://dev.to/pradip_shrestha/why-manual-yuv-video-encoding-fails-across-android-devices-and-how-to-fix-it-5f09</link>
      <guid>https://dev.to/pradip_shrestha/why-manual-yuv-video-encoding-fails-across-android-devices-and-how-to-fix-it-5f09</guid>
      <description>&lt;p&gt;Developing production-quality video recording in Android is deceptively tricky. You might get your app to work perfectly on a Pixel device, only to see green/purple stripes, crashes, or corrupted files on MediaTek or Exynos phones. The root cause? Subtle MediaCodec and YUV layout inconsistencies across OEMs.&lt;/p&gt;

&lt;p&gt;Here’s what I learned after testing dozens of devices and fixing real-world production failures.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many developers take a naive approach: convert a Bitmap to YUV manually and push it to the encoder. It seems simple — but in production, it’s a minefield:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stride assumptions differ per device (MediaTek often pads by 16–64 bytes)&lt;/li&gt;
&lt;li&gt;Chroma plane ordering varies (Planar vs SemiPlanar)&lt;/li&gt;
&lt;li&gt;Surface locking can race, causing IllegalArgumentException&lt;/li&gt;
&lt;li&gt;Encoder output threads can block indefinitely, hanging your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Crashes, corrupted files, and a video pipeline that’s unreliable across devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Naive Approaches Fail Manual YUV Conversion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual YUV Conversion&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun bitmapToYuv420(bitmap: Bitmap, width: Int, height: Int): ByteArray {
    val yuv = ByteArray(width * height * 3 / 2)
    // Naive RGB → YUV conversion without stride handling
    return yuv
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assumes stride == width → fails on MediaTek&lt;/li&gt;
&lt;li&gt;Ignores planar/interleaved layout → color corruption&lt;/li&gt;
&lt;li&gt;Misses hardware alignment → encoder rejects frames&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if it works on your test phone, it’s likely to fail on other devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naive Surface Handling
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun recordFrame(bitmap: Bitmap) {
    val canvas = surface.lockCanvas(null)
    canvas.drawBitmap(bitmap, ...)
    surface.unlockCanvasAndPost(canvas)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No concurrency control → crashes if frame arrives while previous is drawn&lt;/li&gt;
&lt;li&gt;Blocking main thread → dropped frames or camera freezes&lt;/li&gt;
&lt;li&gt;No error recovery → entire recording fails&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Production Solution: Surface-Based Encoding
&lt;/h2&gt;

&lt;p&gt;The only vendor-agnostic, production-ready approach is Surface-based encoding (COLOR_FormatSurface). The hardware handles stride, color conversion, and alignment internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frame dropping: Skip a frame if encoder is busy to prevent Surface lock conflicts&lt;/li&gt;
&lt;li&gt;Short timeout: Use ~100ms for dequeueOutputBuffer() for responsive shutdown&lt;/li&gt;
&lt;li&gt;Resource cleanup order: MediaMuxer → MediaCodec → Surface&lt;/li&gt;
&lt;li&gt;Thread safety: Use a dedicated encoder thread for all operations
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val isEncodingFrame = AtomicBoolean(false)

fun recordFrame(bitmap: Bitmap) {
    if (!isEncodingFrame.compareAndSet(false, true)) return

    try {
        val canvas = encoderSurface.lockCanvas(null)
        canvas.drawBitmap(bitmap, null, dstRect, paint)
        encoderSurface.unlockCanvasAndPost(canvas)
    } finally {
        isEncodingFrame.set(false)
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach works across Qualcomm, MediaTek, and Exynos devices, for Android 10–15, with long recordings and high FPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs and Lessons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Frame dropping vs quality: Slightly choppy video is better than a crash&lt;/li&gt;
&lt;li&gt;Memory &amp;amp; CPU: Surface encoding offloads conversion to GPU, reducing memory footprint&lt;/li&gt;
&lt;li&gt;Background processing: Android 12+ may kill encoder threads if the app is backgrounded — use a foreground service&lt;/li&gt;
&lt;li&gt;Testing: Focus on MediaTek devices (Vivo, Oppo, Xiaomi) and high-FPS scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Avoid manual YUV conversion — it’s fragile and device-specific&lt;/li&gt;
&lt;li&gt;Use Surface-based encoding — hardware handles quirks automatically&lt;/li&gt;
&lt;li&gt;Test on multiple chipsets and Android versions — high signal beats theory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this approach, your video pipeline will be production-ready, reliable, and maintainable — no device-specific hacks required.&lt;/p&gt;

</description>
      <category>android</category>
      <category>mediacodec</category>
      <category>videoencoding</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
