{"id":791,"date":"2026-01-25T09:51:11","date_gmt":"2026-01-25T01:51:11","guid":{"rendered":"https:\/\/ixriver.com\/?p=791"},"modified":"2026-01-25T09:51:11","modified_gmt":"2026-01-25T01:51:11","slug":"%e8%a7%86%e9%a2%91%e6%b8%b2%e6%9f%93","status":"publish","type":"post","link":"https:\/\/www.v.ixriver.com\/?p=791","title":{"rendered":"\u89c6\u9891\u6e32\u67d3"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_65 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title \" >\u76ee\u5f55<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-1'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Video_Rendering\" title=\"Video Rendering\">Video Rendering<\/a><ul class='ez-toc-list-level-2' ><li class='ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%A6%82%E8%BF%B0\" title=\"\u6982\u8ff0\">\u6982\u8ff0<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%B8%B2%E6%9F%93%E6%96%B9%E5%BC%8F\" title=\"\u6e32\u67d3\u65b9\u5f0f\">\u6e32\u67d3\u65b9\u5f0f<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#OpenGLOpenGL_ES\" title=\"OpenGL\/OpenGL ES\">OpenGL\/OpenGL ES<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#DirectXVulkan\" title=\"DirectX\/Vulkan\">DirectX\/Vulkan<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E5%B9%B3%E5%8F%B0%E5%8E%9F%E7%94%9F%E6%B8%B2%E6%9F%93\" title=\"\u5e73\u53f0\u539f\u751f\u6e32\u67d3\">\u5e73\u53f0\u539f\u751f\u6e32\u67d3<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96\" title=\"\u6e32\u67d3\u4f18\u5316\">\u6e32\u67d3\u4f18\u5316<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B8%B2%E6%9F%93\" title=\"\u591a\u7ebf\u7a0b\u6e32\u67d3\">\u591a\u7ebf\u7a0b\u6e32\u67d3<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E5%B8%A7%E5%90%8C%E6%AD%A5%EF%BC%88VSync%EF%BC%89\" title=\"\u5e27\u540c\u6b65\uff08VSync\uff09\">\u5e27\u540c\u6b65\uff08VSync\uff09<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E7%94%BB%E9%9D%A2%E7%BC%A9%E6%94%BE%E4%B8%8E%E8%A3%81%E5%89%AA\" title=\"\u753b\u9762\u7f29\u653e\u4e0e\u88c1\u526a\">\u753b\u9762\u7f29\u653e\u4e0e\u88c1\u526a<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%92%AD%E6%94%BE%E6%8E%A7%E5%88%B6\" title=\"\u64ad\u653e\u63a7\u5236\">\u64ad\u653e\u63a7\u5236<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%92%AD%E6%94%BE%E6%9A%82%E5%81%9C%E5%81%9C%E6%AD%A2\" title=\"\u64ad\u653e\/\u6682\u505c\/\u505c\u6b62\">\u64ad\u653e\/\u6682\u505c\/\u505c\u6b62<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Seek%E5%AE%9A%E4%BD%8D\" title=\"Seek\u5b9a\u4f4d\">Seek\u5b9a\u4f4d<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%92%AD%E6%94%BE%E9%80%9F%E5%BA%A6%E6%8E%A7%E5%88%B6\" title=\"\u64ad\u653e\u901f\u5ea6\u63a7\u5236\">\u64ad\u653e\u901f\u5ea6\u63a7\u5236<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0%E8%AF%A6%E8%A7%A3\" title=\"\u6e32\u67d3\u5b9e\u73b0\u8be6\u89e3\">\u6e32\u67d3\u5b9e\u73b0\u8be6\u89e3<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#iOS_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\" title=\"iOS \u89c6\u9891\u6e32\u67d3\u67b6\u6784\">iOS \u89c6\u9891\u6e32\u67d3\u67b6\u6784<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#iOS_%E7%94%BB%E4%B8%AD%E7%94%BB%EF%BC%88Picture_in_Picture%EF%BC%89%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\" title=\"iOS \u753b\u4e2d\u753b\uff08Picture in Picture\uff09\u6e32\u67d3\u5b9e\u73b0\">iOS \u753b\u4e2d\u753b\uff08Picture in Picture\uff09\u6e32\u67d3\u5b9e\u73b0<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#1_%E7%94%BB%E4%B8%AD%E7%94%BB%E6%9E%B6%E6%9E%84\" title=\"1. \u753b\u4e2d\u753b\u67b6\u6784\">1. \u753b\u4e2d\u753b\u67b6\u6784<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#2_iOS_PiP_%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81\" title=\"2. iOS PiP \u5b9e\u73b0\u4ee3\u7801\">2. iOS PiP \u5b9e\u73b0\u4ee3\u7801<\/a><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#21_%E5%9F%BA%E4%BA%8E_AVPlayer_%E7%9A%84%E7%94%BB%E4%B8%AD%E7%94%BB\" title=\"2.1 \u57fa\u4e8e AVPlayer \u7684\u753b\u4e2d\u753b\">2.1 \u57fa\u4e8e AVPlayer \u7684\u753b\u4e2d\u753b<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#22_%E5%9F%BA%E4%BA%8E_AVSampleBufferDisplayLayer_%E7%9A%84%E7%94%BB%E4%B8%AD%E7%94%BB%EF%BC%88%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A7%A3%E7%A0%81%EF%BC%89\" title=\"2.2 \u57fa\u4e8e AVSampleBufferDisplayLayer \u7684\u753b\u4e2d\u753b\uff08\u81ea\u5b9a\u4e49\u89e3\u7801\uff09\">2.2 \u57fa\u4e8e AVSampleBufferDisplayLayer \u7684\u753b\u4e2d\u753b\uff08\u81ea\u5b9a\u4e49\u89e3\u7801\uff09<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#3_%E7%94%BB%E4%B8%AD%E7%94%BB%E7%8A%B6%E6%80%81%E6%B5%81%E8%BD%AC\" title=\"3. \u753b\u4e2d\u753b\u72b6\u6001\u6d41\u8f6c\">3. \u753b\u4e2d\u753b\u72b6\u6001\u6d41\u8f6c<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#4_%E7%94%BB%E4%B8%AD%E7%94%BB%E6%9D%83%E9%99%90%E9%85%8D%E7%BD%AE\" title=\"4. \u753b\u4e2d\u753b\u6743\u9650\u914d\u7f6e\">4. \u753b\u4e2d\u753b\u6743\u9650\u914d\u7f6e<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Android_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\" title=\"Android \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0\">Android \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#1_Android_%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\" title=\"1. Android \u6e32\u67d3\u67b6\u6784\">1. Android \u6e32\u67d3\u67b6\u6784<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#2_Android_PiP_%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81\" title=\"2. Android PiP \u5b9e\u73b0\u4ee3\u7801\">2. Android PiP \u5b9e\u73b0\u4ee3\u7801<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Metal_%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0%EF%BC%88iOSmacOS%EF%BC%89\" title=\"Metal \u6e32\u67d3\u5b9e\u73b0\uff08iOS\/macOS\uff09\">Metal \u6e32\u67d3\u5b9e\u73b0\uff08iOS\/macOS\uff09<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Metal_%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF\" title=\"Metal \u6e32\u67d3\u7ba1\u7ebf\">Metal \u6e32\u67d3\u7ba1\u7ebf<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Metal_%E6%B8%B2%E6%9F%93%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B\" title=\"Metal \u6e32\u67d3\u4ee3\u7801\u793a\u4f8b\">Metal \u6e32\u67d3\u4ee3\u7801\u793a\u4f8b<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-30\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Metal_%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81\" title=\"Metal \u7740\u8272\u5668\u4ee3\u7801\">Metal \u7740\u8272\u5668\u4ee3\u7801<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-31\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Windows_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\" title=\"Windows \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0\">Windows \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-32\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#1_Windows_%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\" title=\"1. Windows \u6e32\u67d3\u67b6\u6784\">1. Windows \u6e32\u67d3\u67b6\u6784<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-33\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#2_Direct3D_11_%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\" title=\"2. Direct3D 11 \u6e32\u67d3\u5b9e\u73b0\">2. Direct3D 11 \u6e32\u67d3\u5b9e\u73b0<\/a><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class=\"ez-toc-link ez-toc-heading-34\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#21_D3D11_%E6%B8%B2%E6%9F%93%E5%99%A8%E7%B1%BB\" title=\"2.1 D3D11 \u6e32\u67d3\u5668\u7c7b\">2.1 D3D11 \u6e32\u67d3\u5668\u7c7b<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class=\"ez-toc-link ez-toc-heading-35\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#22_HLSL_%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81\" title=\"2.2 HLSL \u7740\u8272\u5668\u4ee3\u7801\">2.2 HLSL \u7740\u8272\u5668\u4ee3\u7801<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-36\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#3_Media_Foundation_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93\" title=\"3. Media Foundation \u89c6\u9891\u6e32\u67d3\">3. Media Foundation \u89c6\u9891\u6e32\u67d3<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-37\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#4_Windows_Desktop_Duplication_API%EF%BC%88%E5%B1%8F%E5%B9%95%E6%8D%95%E8%8E%B7%EF%BC%89\" title=\"4. Windows Desktop Duplication API\uff08\u5c4f\u5e55\u6355\u83b7\uff09\">4. Windows Desktop Duplication API\uff08\u5c4f\u5e55\u6355\u83b7\uff09<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-38\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#5_Windows_%E5%B9%B3%E5%8F%B0%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B%E5%9B%BE\" title=\"5. Windows \u5e73\u53f0\u6e32\u67d3\u6d41\u7a0b\u56fe\">5. Windows \u5e73\u53f0\u6e32\u67d3\u6d41\u7a0b\u56fe<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-39\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#6_WPF_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93\" title=\"6. WPF \u89c6\u9891\u6e32\u67d3\">6. WPF \u89c6\u9891\u6e32\u67d3<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-40\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#7_Windows_%E7%94%BB%E4%B8%AD%E7%94%BB%E5%AE%9E%E7%8E%B0%EF%BC%88Compact_Overlay%EF%BC%89\" title=\"7. Windows \u753b\u4e2d\u753b\u5b9e\u73b0\uff08Compact Overlay\uff09\">7. Windows \u753b\u4e2d\u753b\u5b9e\u73b0\uff08Compact Overlay\uff09<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-41\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#8_Windows_%E6%B8%B2%E6%9F%93%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96\" title=\"8. Windows \u6e32\u67d3\u6027\u80fd\u4f18\u5316\">8. Windows \u6e32\u67d3\u6027\u80fd\u4f18\u5316<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-42\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%B8%B2%E6%9F%93%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94\" title=\"\u6e32\u67d3\u6027\u80fd\u5bf9\u6bd4\">\u6e32\u67d3\u6027\u80fd\u5bf9\u6bd4<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-43\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#Windows_vs_iOS_vs_Android_%E6%B8%B2%E6%9F%93%E5%AF%B9%E6%AF%94\" title=\"Windows vs iOS vs Android \u6e32\u67d3\u5bf9\u6bd4\">Windows vs iOS vs Android \u6e32\u67d3\u5bf9\u6bd4<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-44\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5\" title=\"\u6700\u4f73\u5b9e\u8df5\">\u6700\u4f73\u5b9e\u8df5<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-45\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#1_%E9%80%89%E6%8B%A9%E5%90%88%E9%80%82%E7%9A%84%E6%B8%B2%E6%9F%93%E6%96%B9%E5%BC%8F\" title=\"1. \u9009\u62e9\u5408\u9002\u7684\u6e32\u67d3\u65b9\u5f0f\">1. \u9009\u62e9\u5408\u9002\u7684\u6e32\u67d3\u65b9\u5f0f<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-46\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#2_%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96\" title=\"2. \u6027\u80fd\u4f18\u5316\">2. \u6027\u80fd\u4f18\u5316<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-47\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#3_%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86\" title=\"3. \u5185\u5b58\u7ba1\u7406\">3. \u5185\u5b58\u7ba1\u7406<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-48\" href=\"https:\/\/www.v.ixriver.com\/?p=791\/#%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5\" title=\"\u6545\u969c\u6392\u67e5\">\u6545\u969c\u6392\u67e5<\/a><\/li><\/ul><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<h1><span class=\"ez-toc-section\" id=\"Video_Rendering\"><\/span>Video Rendering<span class=\"ez-toc-section-end\"><\/span><\/h1>\n<h2><span class=\"ez-toc-section\" id=\"%E6%A6%82%E8%BF%B0\"><\/span>\u6982\u8ff0<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>\u89c6\u9891\u6e32\u67d3\u5c06\u89e3\u7801\u540e\u7684\u89c6\u9891\u5e27\u6570\u636e\u8f93\u51fa\u5230\u663e\u793a\u8bbe\u5907\uff08\u5c4f\u5e55\uff09\u8fdb\u884c\u663e\u793a\u3002\u672c\u6587\u6863\u8be6\u7ec6\u4ecb\u7ecd\u4e86\u89c6\u9891\u6e32\u67d3\u7684\u5b9e\u73b0\u65b9\u5f0f\u3001\u4f18\u5316\u7b56\u7565\u4ee5\u53ca\u5404\u5e73\u53f0\u7684\u5177\u4f53\u5b9e\u73b0\uff0c\u7279\u522b\u662f iOS \u7aef\u7684\u753b\u4e2d\u753b\uff08PiP\uff09\u6e32\u67d3\u6280\u672f\u3002<\/p>\n<h2><span class=\"ez-toc-section\" id=\"%E6%B8%B2%E6%9F%93%E6%96%B9%E5%BC%8F\"><\/span>\u6e32\u67d3\u65b9\u5f0f<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<h3><span class=\"ez-toc-section\" id=\"OpenGLOpenGL_ES\"><\/span>OpenGL\/OpenGL ES<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><strong>\u8de8\u5e73\u53f0\u56fe\u5f62API<\/strong>\uff0c\u5e7f\u6cdb\u652f\u6301<\/li>\n<li><strong>OpenGL ES<\/strong> \u662f\u79fb\u52a8\u8bbe\u5907\u7248\u672c<\/li>\n<li>\u652f\u6301\u786c\u4ef6\u52a0\u901f\uff0c\u6027\u80fd\u597d<\/li>\n<li>\u9002\u7528\u4e8e\u5927\u591a\u6570\u5e73\u53f0\u548c\u573a\u666f<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"DirectXVulkan\"><\/span>DirectX\/Vulkan<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><strong>DirectX<\/strong>\uff1aWindows\u5e73\u53f0\u56fe\u5f62API<\/li>\n<li><strong>Vulkan<\/strong>\uff1a\u65b0\u4e00\u4ee3\u4f4e\u5f00\u9500\u56fe\u5f62API\uff0c\u8de8\u5e73\u53f0<\/li>\n<li>\u6027\u80fd\u4f18\u79c0\uff0c\u9002\u5408\u9ad8\u6027\u80fd\u5e94\u7528<\/li>\n<li>Vulkan \u63d0\u4f9b\u66f4\u597d\u7684\u591a\u7ebf\u7a0b\u652f\u6301\u548c\u66f4\u4f4e\u7684\u9a71\u52a8\u5f00\u9500<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"%E5%B9%B3%E5%8F%B0%E5%8E%9F%E7%94%9F%E6%B8%B2%E6%9F%93\"><\/span>\u5e73\u53f0\u539f\u751f\u6e32\u67d3<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><strong>Metal<\/strong>\uff1aApple\u5e73\u53f0\uff08macOS\/iOS\uff09\u56fe\u5f62API<\/li>\n<li><strong>EGL<\/strong>\uff1aOpenGL ES\u7684\u7a97\u53e3\u7cfb\u7edf\u63a5\u53e3<\/li>\n<li>\u5229\u7528\u5e73\u53f0\u7279\u6027\uff0c\u6027\u80fd\u6700\u4f18<\/li>\n<li>\u9488\u5bf9\u7279\u5b9a\u5e73\u53f0\u4f18\u5316\uff0c\u5145\u5206\u5229\u7528\u786c\u4ef6\u80fd\u529b<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96\"><\/span>\u6e32\u67d3\u4f18\u5316<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<h3><span class=\"ez-toc-section\" id=\"%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%B8%B2%E6%9F%93\"><\/span>\u591a\u7ebf\u7a0b\u6e32\u67d3<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>\u89e3\u7801\u548c\u6e32\u67d3\u5206\u79bb\u5230\u4e0d\u540c\u7ebf\u7a0b<\/li>\n<li>\u907f\u514d\u963b\u585e\u4e3b\u7ebf\u7a0b<\/li>\n<li>\u63d0\u9ad8\u6d41\u7545\u5ea6\u548c\u54cd\u5e94\u6027<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"%E5%B8%A7%E5%90%8C%E6%AD%A5%EF%BC%88VSync%EF%BC%89\"><\/span>\u5e27\u540c\u6b65\uff08VSync\uff09<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>\u4e0e\u663e\u793a\u5668\u5237\u65b0\u7387\u540c\u6b65<\/li>\n<li>\u907f\u514d\u753b\u9762\u6495\u88c2<\/li>\n<li>\u964d\u4f4e\u529f\u8017<\/li>\n<li>\u63d0\u4f9b\u66f4\u6d41\u7545\u7684\u89c6\u89c9\u4f53\u9a8c<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"%E7%94%BB%E9%9D%A2%E7%BC%A9%E6%94%BE%E4%B8%8E%E8%A3%81%E5%89%AA\"><\/span>\u753b\u9762\u7f29\u653e\u4e0e\u88c1\u526a<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><strong>\u9ad8\u6548\u7f29\u653e\u7b97\u6cd5<\/strong>\uff1a\u53cc\u7ebf\u6027\u3001\u53cc\u4e09\u6b21\u63d2\u503c<\/li>\n<li><strong>\u786c\u4ef6\u52a0\u901f\u7f29\u653e<\/strong>\uff1a\u5229\u7528GPU\u8fdb\u884c\u7f29\u653e\u64cd\u4f5c<\/li>\n<li><strong>\u652f\u6301\u88c1\u526a\u548c\u65cb\u8f6c<\/strong>\uff1a\u7075\u6d3b\u7684\u753b\u9762\u5904\u7406\u80fd\u529b<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"%E6%92%AD%E6%94%BE%E6%8E%A7%E5%88%B6\"><\/span>\u64ad\u653e\u63a7\u5236<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<h3><span class=\"ez-toc-section\" id=\"%E6%92%AD%E6%94%BE%E6%9A%82%E5%81%9C%E5%81%9C%E6%AD%A2\"><\/span>\u64ad\u653e\/\u6682\u505c\/\u505c\u6b62<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>\u63a7\u5236\u89e3\u7801\u548c\u6e32\u67d3\u6d41\u7a0b<\/li>\n<li>\u72b6\u6001\u7ba1\u7406<\/li>\n<li>\u8d44\u6e90\u91ca\u653e<\/li>\n<li>\u63d0\u4f9b\u57fa\u672c\u7684\u64ad\u653e\u63a7\u5236\u529f\u80fd<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"Seek%E5%AE%9A%E4%BD%8D\"><\/span>Seek\u5b9a\u4f4d<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>\u5feb\u901f\u5b9a\u4f4d\u5230\u6307\u5b9a\u65f6\u95f4\u70b9<\/li>\n<li>\u67e5\u627e\u6700\u8fd1\u7684\u5173\u952e\u5e27<\/li>\n<li>\u9884\u52a0\u8f7d\u6570\u636e<\/li>\n<li>\u4f18\u5316\u5b9a\u4f4d\u6027\u80fd\uff0c\u51cf\u5c11\u7b49\u5f85\u65f6\u95f4<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"%E6%92%AD%E6%94%BE%E9%80%9F%E5%BA%A6%E6%8E%A7%E5%88%B6\"><\/span>\u64ad\u653e\u901f\u5ea6\u63a7\u5236<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li>\u652f\u6301\u500d\u901f\u64ad\u653e\uff080.5x\u30011.5x\u30012x\u7b49\uff09<\/li>\n<li>\u8c03\u6574\u89e3\u7801\u548c\u6e32\u67d3\u901f\u5ea6<\/li>\n<li>\u4fdd\u6301\u97f3\u89c6\u9891\u540c\u6b65<\/li>\n<li>\u63d0\u4f9b\u7075\u6d3b\u7684\u64ad\u653e\u901f\u5ea6\u8c03\u6574\u80fd\u529b<\/li>\n<\/ul>\n<hr \/>\n<h2><span class=\"ez-toc-section\" id=\"%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0%E8%AF%A6%E8%A7%A3\"><\/span>\u6e32\u67d3\u5b9e\u73b0\u8be6\u89e3<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<h3><span class=\"ez-toc-section\" id=\"iOS_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\"><\/span>iOS \u89c6\u9891\u6e32\u67d3\u67b6\u6784<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre><code class=\"language-mermaid\">graph TB\n    subgraph &quot;\u89c6\u9891\u6e90&quot;\n        VideoDecoder[\u89c6\u9891\u89e3\u7801\u5668]\n        CVPixelBuffer[CVPixelBuffer]\n    end\n\n    subgraph &quot;\u6e32\u67d3\u5c42&quot;\n        AVSampleBufferDisplayLayer[AVSampleBufferDisplayLayer]\n        CALayer[CALayer]\n        Metal[Metal\u6e32\u67d3]\n        OpenGLES[OpenGL ES\u6e32\u67d3]\n    end\n\n    subgraph &quot;\u663e\u793a\u5c42&quot;\n        UIView[UIView]\n        AVPlayerLayer[AVPlayerLayer]\n        PiPController[\u753b\u4e2d\u753b\u63a7\u5236\u5668]\n    end\n\n    VideoDecoder --&gt; CVPixelBuffer\n    CVPixelBuffer --&gt; AVSampleBufferDisplayLayer\n    CVPixelBuffer --&gt; Metal\n    CVPixelBuffer --&gt; OpenGLES\n\n    AVSampleBufferDisplayLayer --&gt; CALayer\n    Metal --&gt; CALayer\n    OpenGLES --&gt; CALayer\n\n    CALayer --&gt; UIView\n    CALayer --&gt; AVPlayerLayer\n    AVPlayerLayer --&gt; PiPController\n\n    style PiPController fill:#e74c3c,stroke:#c0392b,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<h3><span class=\"ez-toc-section\" id=\"iOS_%E7%94%BB%E4%B8%AD%E7%94%BB%EF%BC%88Picture_in_Picture%EF%BC%89%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\"><\/span>iOS \u753b\u4e2d\u753b\uff08Picture in Picture\uff09\u6e32\u67d3\u5b9e\u73b0<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"1_%E7%94%BB%E4%B8%AD%E7%94%BB%E6%9E%B6%E6%9E%84\"><\/span>1. \u753b\u4e2d\u753b\u67b6\u6784<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">graph TD\n    subgraph &quot;\u5e94\u7528\u5c42&quot;\n        ViewController[ViewController]\n        VideoView[\u89c6\u9891\u64ad\u653e\u89c6\u56fe]\n    end\n\n    subgraph &quot;AVFoundation&quot;\n        AVPictureInPictureController[AVPictureInPictureController]\n        AVPlayerLayer[AVPlayerLayer]\n        AVPlayer[AVPlayer]\n        AVSampleBufferDisplayLayer[AVSampleBufferDisplayLayer]\n    end\n\n    subgraph &quot;\u7cfb\u7edf\u5c42&quot;\n        PiPWindow[\u7cfb\u7edf\u753b\u4e2d\u753b\u7a97\u53e3]\n        SystemUI[\u7cfb\u7edfUI\u63a7\u5236]\n    end\n\n    ViewController --&gt; AVPictureInPictureController\n    VideoView --&gt; AVPlayerLayer\n    AVPlayerLayer --&gt; AVPlayer\n\n    AVPictureInPictureController -.-&gt;|\u63a7\u5236| AVPlayerLayer\n    AVPictureInPictureController -.-&gt;|\u6216\u63a7\u5236| AVSampleBufferDisplayLayer\n\n    AVPictureInPictureController --&gt; PiPWindow\n    PiPWindow --&gt; SystemUI\n\n    style AVPictureInPictureController fill:#3498db,stroke:#2980b9,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"2_iOS_PiP_%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81\"><\/span>2. iOS PiP \u5b9e\u73b0\u4ee3\u7801<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<h5><span class=\"ez-toc-section\" id=\"21_%E5%9F%BA%E4%BA%8E_AVPlayer_%E7%9A%84%E7%94%BB%E4%B8%AD%E7%94%BB\"><\/span>2.1 \u57fa\u4e8e AVPlayer \u7684\u753b\u4e2d\u753b<span class=\"ez-toc-section-end\"><\/span><\/h5>\n<pre><code class=\"language-swift\">import AVKit\nimport AVFoundation\n\nclass VideoPlayerViewController: UIViewController {\n\n    \/\/ MARK: - Properties\n    private var player: AVPlayer!\n    private var playerLayer: AVPlayerLayer!\n    private var pipController: AVPictureInPictureController?\n\n    \/\/ MARK: - Setup\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupPlayer()\n        setupPictureInPicture()\n    }\n\n    private func setupPlayer() {\n        \/\/ \u521b\u5efa\u64ad\u653e\u5668\n        let url = URL(string: &quot;https:\/\/example.com\/video.mp4&quot;)!\n        player = AVPlayer(url: url)\n\n        \/\/ \u521b\u5efa\u64ad\u653e\u5668\u56fe\u5c42\n        playerLayer = AVPlayerLayer(player: player)\n        playerLayer.frame = view.bounds\n        playerLayer.videoGravity = .resizeAspect\n        view.layer.addSublayer(playerLayer)\n    }\n\n    private func setupPictureInPicture() {\n        \/\/ \u68c0\u67e5\u8bbe\u5907\u662f\u5426\u652f\u6301\u753b\u4e2d\u753b\n        guard AVPictureInPictureController.isPictureInPictureSupported() else {\n            print(&quot;\u753b\u4e2d\u753b\u4e0d\u652f\u6301&quot;)\n            return\n        }\n\n        \/\/ \u521b\u5efa\u753b\u4e2d\u753b\u63a7\u5236\u5668\n        pipController = AVPictureInPictureController(playerLayer: playerLayer)\n        pipController?.delegate = self\n\n        \/\/ \u914d\u7f6e\u97f3\u9891\u4f1a\u8bdd\n        try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)\n        try? AVAudioSession.sharedInstance().setActive(true)\n    }\n\n    \/\/ MARK: - PiP Control\n    @IBAction func togglePictureInPicture(_ sender: UIButton) {\n        if pipController?.isPictureInPictureActive == true {\n            \/\/ \u505c\u6b62\u753b\u4e2d\u753b\n            pipController?.stopPictureInPicture()\n        } else {\n            \/\/ \u5f00\u542f\u753b\u4e2d\u753b\n            pipController?.startPictureInPicture()\n        }\n    }\n}\n\n\/\/ MARK: - AVPictureInPictureControllerDelegate\nextension VideoPlayerViewController: AVPictureInPictureControllerDelegate {\n\n    func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u753b\u4e2d\u753b\u5373\u5c06\u5f00\u59cb&quot;)\n    }\n\n    func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u753b\u4e2d\u753b\u5df2\u5f00\u59cb&quot;)\n    }\n\n    func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u753b\u4e2d\u753b\u5373\u5c06\u505c\u6b62&quot;)\n    }\n\n    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u753b\u4e2d\u753b\u5df2\u505c\u6b62&quot;)\n    }\n\n    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, \n                                   failedToStartPictureInPictureWithError error: Error) {\n        print(&quot;\u753b\u4e2d\u753b\u542f\u52a8\u5931\u8d25: \\(error.localizedDescription)&quot;)\n    }\n\n    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, \n                                   restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -&gt; Void) {\n        \/\/ \u6062\u590d\u7528\u6237\u754c\u9762\n        \/\/ \u4f8b\u5982\uff1a\u5c55\u793a\u64ad\u653e\u5668\u89c6\u56fe\u63a7\u5236\u5668\n        completionHandler(true)\n    }\n}\n<\/code><\/pre>\n<h5><span class=\"ez-toc-section\" id=\"22_%E5%9F%BA%E4%BA%8E_AVSampleBufferDisplayLayer_%E7%9A%84%E7%94%BB%E4%B8%AD%E7%94%BB%EF%BC%88%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A7%A3%E7%A0%81%EF%BC%89\"><\/span>2.2 \u57fa\u4e8e AVSampleBufferDisplayLayer \u7684\u753b\u4e2d\u753b\uff08\u81ea\u5b9a\u4e49\u89e3\u7801\uff09<span class=\"ez-toc-section-end\"><\/span><\/h5>\n<pre><code class=\"language-swift\">import AVKit\nimport AVFoundation\n\nclass CustomVideoRenderer: NSObject {\n\n    \/\/ MARK: - Properties\n    private var sampleBufferDisplayLayer: AVSampleBufferDisplayLayer!\n    private var pipController: AVPictureInPictureController?\n    private var videoCallDelegate: AVPictureInPictureSampleBufferPlaybackDelegate!\n\n    \/\/ MARK: - Setup\n    func setupCustomPictureInPicture() {\n        \/\/ \u521b\u5efa AVSampleBufferDisplayLayer\n        sampleBufferDisplayLayer = AVSampleBufferDisplayLayer()\n        sampleBufferDisplayLayer.videoGravity = .resizeAspect\n\n        \/\/ \u521b\u5efa\u64ad\u653e\u4ee3\u7406\n        videoCallDelegate = VideoCallPlaybackDelegate()\n\n        \/\/ \u521b\u5efa\u753b\u4e2d\u753b\u63a7\u5236\u5668\uff08iOS 15+\uff09\n        if #available(iOS 15.0, *) {\n            let contentSource = AVPictureInPictureController.ContentSource(\n                sampleBufferDisplayLayer: sampleBufferDisplayLayer,\n                playbackDelegate: videoCallDelegate\n            )\n\n            pipController = AVPictureInPictureController(contentSource: contentSource)\n            pipController?.delegate = self\n            pipController?.canStartPictureInPictureAutomaticallyFromInline = true\n        }\n    }\n\n    \/\/ MARK: - Render Frame\n    func renderVideoFrame(_ sampleBuffer: CMSampleBuffer) {\n        guard sampleBufferDisplayLayer.isReadyForMoreMediaData else {\n            print(&quot;DisplayLayer \u672a\u51c6\u5907\u597d&quot;)\n            return\n        }\n\n        \/\/ \u6e32\u67d3\u89c6\u9891\u5e27\n        sampleBufferDisplayLayer.enqueue(sampleBuffer)\n    }\n\n    func renderPixelBuffer(_ pixelBuffer: CVPixelBuffer, presentationTime: CMTime) {\n        \/\/ \u4ece CVPixelBuffer \u521b\u5efa CMSampleBuffer\n        var sampleBuffer: CMSampleBuffer?\n        var formatDescription: CMFormatDescription?\n\n        CMVideoFormatDescriptionCreateForImageBuffer(\n            allocator: kCFAllocatorDefault,\n            imageBuffer: pixelBuffer,\n            formatDescriptionOut: &amp;formatDescription\n        )\n\n        guard let formatDesc = formatDescription else { return }\n\n        var timingInfo = CMSampleTimingInfo(\n            duration: .invalid,\n            presentationTimeStamp: presentationTime,\n            decodeTimeStamp: .invalid\n        )\n\n        CMSampleBufferCreateReadyWithImageBuffer(\n            allocator: kCFAllocatorDefault,\n            imageBuffer: pixelBuffer,\n            formatDescription: formatDesc,\n            sampleTiming: &amp;timingInfo,\n            sampleBufferOut: &amp;sampleBuffer\n        )\n\n        if let buffer = sampleBuffer {\n            renderVideoFrame(buffer)\n        }\n    }\n}\n\n\/\/ MARK: - Playback Delegate\n@available(iOS 15.0, *)\nclass VideoCallPlaybackDelegate: NSObject, AVPictureInPictureSampleBufferPlaybackDelegate {\n\n    var isPlaying = false\n    var timeRange: CMTimeRange = .zero\n\n    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, \n                                   setPlaying playing: Bool) {\n        isPlaying = playing\n        \/\/ \u901a\u77e5\u4e1a\u52a1\u5c42\u64ad\u653e\u72b6\u6001\u53d8\u5316\n        NotificationCenter.default.post(name: .pipPlaybackStateChanged, object: playing)\n    }\n\n    func pictureInPictureControllerTimeRangeForPlayback(_ pictureInPictureController: AVPictureInPictureController) -&gt; CMTimeRange {\n        return timeRange\n    }\n\n    func pictureInPictureControllerIsPlaybackPaused(_ pictureInPictureController: AVPictureInPictureController) -&gt; Bool {\n        return !isPlaying\n    }\n\n    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, \n                                   didTransitionToRenderSize newRenderSize: CMVideoDimensions) {\n        print(&quot;\u753b\u4e2d\u753b\u7a97\u53e3\u5927\u5c0f\u53d8\u5316: \\(newRenderSize.width)x\\(newRenderSize.height)&quot;)\n    }\n\n    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, \n                                   skipByInterval skipInterval: CMTime) async {\n        \/\/ \u5904\u7406\u5feb\u8fdb\/\u5feb\u9000\n        print(&quot;\u8df3\u8f6c: \\(skipInterval.seconds) \u79d2&quot;)\n    }\n}\n\nextension Notification.Name {\n    static let pipPlaybackStateChanged = Notification.Name(&quot;pipPlaybackStateChanged&quot;)\n}\n\n\/\/ MARK: - PiP Delegate\nextension CustomVideoRenderer: AVPictureInPictureControllerDelegate {\n    func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u81ea\u5b9a\u4e49\u753b\u4e2d\u753b\u5df2\u5f00\u59cb&quot;)\n    }\n\n    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {\n        print(&quot;\u81ea\u5b9a\u4e49\u753b\u4e2d\u753b\u5df2\u505c\u6b62&quot;)\n    }\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"3_%E7%94%BB%E4%B8%AD%E7%94%BB%E7%8A%B6%E6%80%81%E6%B5%81%E8%BD%AC\"><\/span>3. \u753b\u4e2d\u753b\u72b6\u6001\u6d41\u8f6c<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">stateDiagram-v2\n    [*] --&gt; Idle: \u521d\u59cb\u5316\n\n    Idle --&gt; Starting: startPictureInPicture()\n    Starting --&gt; Active: \u542f\u52a8\u6210\u529f\n    Starting --&gt; Failed: \u542f\u52a8\u5931\u8d25\n    Failed --&gt; Idle: \u91cd\u7f6e\n\n    Active --&gt; Stopping: stopPictureInPicture()\n    Active --&gt; Active: \u7528\u6237\u4ea4\u4e92\n\n    Stopping --&gt; Idle: \u505c\u6b62\u5b8c\u6210\n\n    Active --&gt; Restoring: \u7528\u6237\u70b9\u51fb\u8fd4\u56de\n    Restoring --&gt; Idle: \u6062\u590d\u4e3b\u754c\u9762\n\n    note right of Active\n        \u753b\u4e2d\u753b\u7a97\u53e3\u663e\u793a\n        \u652f\u6301\u62d6\u52a8\u3001\u8c03\u6574\u5927\u5c0f\n        \u663e\u793a\u64ad\u653e\u63a7\u5236\n    end note\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"4_%E7%94%BB%E4%B8%AD%E7%94%BB%E6%9D%83%E9%99%90%E9%85%8D%E7%BD%AE\"><\/span>4. \u753b\u4e2d\u753b\u6743\u9650\u914d\u7f6e<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p>\u5728 <code>Info.plist<\/code> \u4e2d\u6dfb\u52a0\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;key&gt;UIBackgroundModes&lt;\/key&gt;\n&lt;array&gt;\n    &lt;string&gt;audio&lt;\/string&gt;\n&lt;\/array&gt;\n<\/code><\/pre>\n<p>\u5728 Xcode \u9879\u76ee\u8bbe\u7f6e\u4e2d\u542f\u7528\uff1a<br \/>\n&#8211; <strong>Signing &amp; Capabilities<\/strong> \u2192 <strong>+ Capability<\/strong> \u2192 <strong>Background Modes<\/strong><br \/>\n&#8211; \u52fe\u9009 <strong>Audio, AirPlay, and Picture in Picture<\/strong><\/p>\n<h3><span class=\"ez-toc-section\" id=\"Android_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\"><\/span>Android \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"1_Android_%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\"><\/span>1. Android \u6e32\u67d3\u67b6\u6784<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">graph TB\n    subgraph &quot;\u89c6\u9891\u6e90&quot;\n        MediaCodec[MediaCodec]\n        Surface[Surface]\n    end\n\n    subgraph &quot;\u6e32\u67d3\u65b9\u5f0f&quot;\n        SurfaceView[SurfaceView]\n        TextureView[TextureView]\n        GLSurfaceView[GLSurfaceView]\n    end\n\n    subgraph &quot;PiP\u652f\u6301&quot;\n        PictureInPictureParams[PiP\u53c2\u6570\u914d\u7f6e]\n        Activity[Activity.enterPictureInPictureMode]\n    end\n\n    MediaCodec --&gt; Surface\n    Surface --&gt; SurfaceView\n    Surface --&gt; TextureView\n    Surface --&gt; GLSurfaceView\n\n    Activity --&gt; PictureInPictureParams\n\n    style Activity fill:#4CAF50,stroke:#388E3C,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"2_Android_PiP_%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81\"><\/span>2. Android PiP \u5b9e\u73b0\u4ee3\u7801<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-kotlin\">import android.app.PictureInPictureParams\nimport android.content.res.Configuration\nimport android.os.Build\nimport android.util.Rational\nimport androidx.appcompat.app.AppCompatActivity\n\nclass VideoPlayerActivity : AppCompatActivity() {\n\n    private lateinit var videoView: VideoView\n    private var isInPipMode = false\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_video_player)\n\n        videoView = findViewById(R.id.videoView)\n        setupVideoPlayer()\n    }\n\n    private fun setupVideoPlayer() {\n        val videoUrl = &quot;https:\/\/example.com\/video.mp4&quot;\n        videoView.setVideoPath(videoUrl)\n        videoView.start()\n    }\n\n    \/\/ \u8fdb\u5165\u753b\u4e2d\u753b\u6a21\u5f0f\n    fun enterPictureInPictureMode() {\n        if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O) {\n            val aspectRatio = Rational(16, 9)\n\n            val params = PictureInPictureParams.Builder()\n                .setAspectRatio(aspectRatio)\n                .build()\n\n            enterPictureInPictureMode(params)\n        }\n    }\n\n    override fun onUserLeaveHint() {\n        super.onUserLeaveHint()\n        \/\/ \u7528\u6237\u6309 Home \u952e\u65f6\u81ea\u52a8\u8fdb\u5165\u753b\u4e2d\u753b\n        if (videoView.isPlaying) {\n            enterPictureInPictureMode()\n        }\n    }\n\n    override fun onPictureInPictureModeChanged(\n        isInPictureInPictureMode: Boolean,\n        newConfig: Configuration\n    ) {\n        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)\n\n        isInPipMode = isInPictureInPictureMode\n\n        if (isInPictureInPictureMode) {\n            \/\/ \u8fdb\u5165\u753b\u4e2d\u753b\u6a21\u5f0f\uff0c\u9690\u85cf\u63a7\u5236UI\n            hideControls()\n        } else {\n            \/\/ \u9000\u51fa\u753b\u4e2d\u753b\u6a21\u5f0f\uff0c\u663e\u793a\u63a7\u5236UI\n            showControls()\n        }\n    }\n\n    private fun hideControls() {\n        \/\/ \u9690\u85cf\u64ad\u653e\u63a7\u5236\u3001\u6807\u9898\u680f\u7b49\n        supportActionBar?.hide()\n    }\n\n    private fun showControls() {\n        \/\/ \u663e\u793a\u64ad\u653e\u63a7\u5236\u3001\u6807\u9898\u680f\u7b49\n        supportActionBar?.show()\n    }\n}\n<\/code><\/pre>\n<h3><span class=\"ez-toc-section\" id=\"Metal_%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0%EF%BC%88iOSmacOS%EF%BC%89\"><\/span>Metal \u6e32\u67d3\u5b9e\u73b0\uff08iOS\/macOS\uff09<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"Metal_%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF\"><\/span>Metal \u6e32\u67d3\u7ba1\u7ebf<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">graph LR\n    subgraph &quot;\u6e32\u67d3\u7ba1\u7ebf&quot;\n        CVPixelBuffer[CVPixelBuffer]\n        MTLTexture[Metal\u7eb9\u7406]\n        VertexShader[\u9876\u70b9\u7740\u8272\u5668]\n        FragmentShader[\u7247\u6bb5\u7740\u8272\u5668]\n        MTLRenderCommandEncoder[\u6e32\u67d3\u547d\u4ee4\u7f16\u7801\u5668]\n        MTLCommandBuffer[\u547d\u4ee4\u7f13\u51b2\u533a]\n        Display[\u663e\u793a]\n    end\n\n    CVPixelBuffer --&gt;|\u7eb9\u7406\u6620\u5c04| MTLTexture\n    MTLTexture --&gt; VertexShader\n    VertexShader --&gt; FragmentShader\n    FragmentShader --&gt; MTLRenderCommandEncoder\n    MTLRenderCommandEncoder --&gt; MTLCommandBuffer\n    MTLCommandBuffer --&gt;|\u63d0\u4ea4| Display\n\n    style MTLTexture fill:#9b59b6,stroke:#8e44ad,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"Metal_%E6%B8%B2%E6%9F%93%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B\"><\/span>Metal \u6e32\u67d3\u4ee3\u7801\u793a\u4f8b<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-swift\">import MetalKit\n\nclass MetalVideoRenderer: NSObject, MTKViewDelegate {\n\n    var device: MTLDevice!\n    var commandQueue: MTLCommandQueue!\n    var pipelineState: MTLRenderPipelineState!\n    var textureCache: CVMetalTextureCache!\n\n    \/\/ MARK: - Setup\n    func setupMetal(metalView: MTKView) {\n        \/\/ \u521b\u5efa\u8bbe\u5907\n        device = MTLCreateSystemDefaultDevice()\n        metalView.device = device\n        metalView.delegate = self\n\n        \/\/ \u521b\u5efa\u547d\u4ee4\u961f\u5217\n        commandQueue = device.makeCommandQueue()\n\n        \/\/ \u521b\u5efa\u7eb9\u7406\u7f13\u5b58\n        CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &amp;textureCache)\n\n        \/\/ \u521b\u5efa\u6e32\u67d3\u7ba1\u7ebf\n        setupPipeline()\n    }\n\n    private func setupPipeline() {\n        let library = device.makeDefaultLibrary()\n        let vertexFunction = library?.makeFunction(name: &quot;vertexShader&quot;)\n        let fragmentFunction = library?.makeFunction(name: &quot;fragmentShader&quot;)\n\n        let pipelineDescriptor = MTLRenderPipelineDescriptor()\n        pipelineDescriptor.vertexFunction = vertexFunction\n        pipelineDescriptor.fragmentFunction = fragmentFunction\n        pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm\n\n        pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)\n    }\n\n    \/\/ MARK: - Render\n    func renderPixelBuffer(_ pixelBuffer: CVPixelBuffer, in view: MTKView) {\n        \/\/ \u4ece CVPixelBuffer \u521b\u5efa Metal \u7eb9\u7406\n        var textureRef: CVMetalTexture?\n        let width = CVPixelBufferGetWidth(pixelBuffer)\n        let height = CVPixelBufferGetHeight(pixelBuffer)\n\n        CVMetalTextureCacheCreateTextureFromImage(\n            kCFAllocatorDefault,\n            textureCache,\n            pixelBuffer,\n            nil,\n            .bgra8Unorm,\n            width,\n            height,\n            0,\n            &amp;textureRef\n        )\n\n        guard let cvTexture = textureRef,\n              let texture = CVMetalTextureGetTexture(cvTexture) else {\n            return\n        }\n\n        \/\/ \u521b\u5efa\u547d\u4ee4\u7f13\u51b2\u533a\n        guard let commandBuffer = commandQueue.makeCommandBuffer(),\n              let renderPassDescriptor = view.currentRenderPassDescriptor,\n              let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {\n            return\n        }\n\n        \/\/ \u8bbe\u7f6e\u6e32\u67d3\u72b6\u6001\n        renderEncoder.setRenderPipelineState(pipelineState)\n        renderEncoder.setFragmentTexture(texture, index: 0)\n\n        \/\/ \u7ed8\u5236\n        renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)\n        renderEncoder.endEncoding()\n\n        \/\/ \u663e\u793a\n        if let drawable = view.currentDrawable {\n            commandBuffer.present(drawable)\n        }\n\n        commandBuffer.commit()\n    }\n\n    \/\/ MARK: - MTKViewDelegate\n    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {\n        \/\/ \u5904\u7406\u89c6\u56fe\u5927\u5c0f\u53d8\u5316\n    }\n\n    func draw(in view: MTKView) {\n        \/\/ \u6bcf\u5e27\u8c03\u7528\n    }\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"Metal_%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81\"><\/span>Metal \u7740\u8272\u5668\u4ee3\u7801<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-metal\">\/\/ Shaders.metal\n#include &lt;metal_stdlib&gt;\nusing namespace metal;\n\nstruct VertexOut {\n    float4 position [[position]];\n    float2 texCoord;\n};\n\n\/\/ \u9876\u70b9\u7740\u8272\u5668\nvertex VertexOut vertexShader(uint vertexID [[vertex_id]]) {\n    float2 positions[4] = {\n        float2(-1.0, -1.0),\n        float2( 1.0, -1.0),\n        float2(-1.0,  1.0),\n        float2( 1.0,  1.0)\n    };\n\n    float2 texCoords[4] = {\n        float2(0.0, 1.0),\n        float2(1.0, 1.0),\n        float2(0.0, 0.0),\n        float2(1.0, 0.0)\n    };\n\n    VertexOut out;\n    out.position = float4(positions[vertexID], 0.0, 1.0);\n    out.texCoord = texCoords[vertexID];\n    return out;\n}\n\n\/\/ \u7247\u6bb5\u7740\u8272\u5668\nfragment float4 fragmentShader(VertexOut in [[stage_in]],\n                              texture2d&lt;float&gt; texture [[texture(0)]]) {\n    constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);\n    return texture.sample(textureSampler, in.texCoord);\n}\n<\/code><\/pre>\n<h3><span class=\"ez-toc-section\" id=\"Windows_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\"><\/span>Windows \u89c6\u9891\u6e32\u67d3\u5b9e\u73b0<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"1_Windows_%E6%B8%B2%E6%9F%93%E6%9E%B6%E6%9E%84\"><\/span>1. Windows \u6e32\u67d3\u67b6\u6784<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">graph TB\n    subgraph &quot;\u89c6\u9891\u6e90&quot;\n        Decoder[\u89c6\u9891\u89e3\u7801\u5668&lt;br\/&gt;FFmpeg\/MediaFoundation]\n        VideoFrame[\u89c6\u9891\u5e27\u6570\u636e]\n    end\n\n    subgraph &quot;\u6e32\u67d3API&quot;\n        D3D11[Direct3D 11]\n        D3D12[Direct3D 12]\n        OpenGL[OpenGL]\n        GDI[GDI\/GDI+]\n    end\n\n    subgraph &quot;\u7a97\u53e3\u7cfb\u7edf&quot;\n        HWND[\u7a97\u53e3\u53e5\u67c4]\n        SwapChain[\u4ea4\u6362\u94fe]\n        Present[\u663e\u793a]\n    end\n\n    Decoder --&gt; VideoFrame\n    VideoFrame --&gt; D3D11\n    VideoFrame --&gt; D3D12\n    VideoFrame --&gt; OpenGL\n    VideoFrame --&gt; GDI\n\n    D3D11 --&gt; SwapChain\n    D3D12 --&gt; SwapChain\n    OpenGL --&gt; HWND\n    GDI --&gt; HWND\n\n    SwapChain --&gt; Present\n    HWND --&gt; Present\n\n    style D3D11 fill:#0078d4,stroke:#005a9e,stroke-width:2px,color:#fff\n    style D3D12 fill:#0078d4,stroke:#005a9e,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"2_Direct3D_11_%E6%B8%B2%E6%9F%93%E5%AE%9E%E7%8E%B0\"><\/span>2. Direct3D 11 \u6e32\u67d3\u5b9e\u73b0<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<h5><span class=\"ez-toc-section\" id=\"21_D3D11_%E6%B8%B2%E6%9F%93%E5%99%A8%E7%B1%BB\"><\/span>2.1 D3D11 \u6e32\u67d3\u5668\u7c7b<span class=\"ez-toc-section-end\"><\/span><\/h5>\n<pre><code class=\"language-cpp\">#include &lt;d3d11.h&gt;\n#include &lt;dxgi.h&gt;\n#include &lt;wrl\/client.h&gt;\n#include &lt;DirectXMath.h&gt;\n\nusing Microsoft::WRL::ComPtr;\nusing namespace DirectX;\n\nclass D3D11VideoRenderer {\npublic:\n    D3D11VideoRenderer(HWND hwnd);\n    ~D3D11VideoRenderer();\n\n    bool Initialize();\n    bool RenderFrame(uint8_t* yuvData, int width, int height);\n    void Resize(int width, int height);\n    void Cleanup();\n\nprivate:\n    bool CreateDeviceAndSwapChain();\n    bool CreateRenderTarget();\n    bool CreateShaders();\n    bool CreateTextures(int width, int height);\n    void UpdateYUVTextures(uint8_t* yData, uint8_t* uData, uint8_t* vData, \n                          int width, int height);\n\nprivate:\n    HWND m_hwnd;\n\n    \/\/ D3D11 \u6838\u5fc3\u5bf9\u8c61\n    ComPtr&lt;ID3D11Device&gt; m_device;\n    ComPtr&lt;ID3D11DeviceContext&gt; m_context;\n    ComPtr&lt;IDXGISwapChain&gt; m_swapChain;\n    ComPtr&lt;ID3D11RenderTargetView&gt; m_renderTargetView;\n\n    \/\/ \u7740\u8272\u5668\u8d44\u6e90\n    ComPtr&lt;ID3D11VertexShader&gt; m_vertexShader;\n    ComPtr&lt;ID3D11PixelShader&gt; m_pixelShader;\n    ComPtr&lt;ID3D11InputLayout&gt; m_inputLayout;\n\n    \/\/ \u7eb9\u7406\u8d44\u6e90\uff08YUV\uff09\n    ComPtr&lt;ID3D11Texture2D&gt; m_textureY;\n    ComPtr&lt;ID3D11Texture2D&gt; m_textureU;\n    ComPtr&lt;ID3D11Texture2D&gt; m_textureV;\n    ComPtr&lt;ID3D11ShaderResourceView&gt; m_srvY;\n    ComPtr&lt;ID3D11ShaderResourceView&gt; m_srvU;\n    ComPtr&lt;ID3D11ShaderResourceView&gt; m_srvV;\n\n    \/\/ \u91c7\u6837\u5668\n    ComPtr&lt;ID3D11SamplerState&gt; m_sampler;\n\n    \/\/ \u9876\u70b9\u7f13\u51b2\u533a\n    ComPtr&lt;ID3D11Buffer&gt; m_vertexBuffer;\n\n    int m_width = 0;\n    int m_height = 0;\n};\n\n\/\/ \u6784\u9020\u51fd\u6570\nD3D11VideoRenderer::D3D11VideoRenderer(HWND hwnd) \n    : m_hwnd(hwnd) {\n}\n\n\/\/ \u6790\u6784\u51fd\u6570\nD3D11VideoRenderer::~D3D11VideoRenderer() {\n    Cleanup();\n}\n\n\/\/ \u521d\u59cb\u5316\nbool D3D11VideoRenderer::Initialize() {\n    if (!CreateDeviceAndSwapChain()) return false;\n    if (!CreateRenderTarget()) return false;\n    if (!CreateShaders()) return false;\n\n    \/\/ \u521b\u5efa\u91c7\u6837\u5668\u72b6\u6001\n    D3D11_SAMPLER_DESC samplerDesc = {};\n    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;\n    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;\n    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;\n    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;\n    samplerDesc.MaxAnisotropy = 1;\n    samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;\n    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;\n\n    HRESULT hr = m_device-&gt;CreateSamplerState(&amp;samplerDesc, &amp;m_sampler);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa\u9876\u70b9\u7f13\u51b2\u533a\n    struct Vertex {\n        XMFLOAT3 position;\n        XMFLOAT2 texCoord;\n    };\n\n    Vertex vertices[] = {\n        { XMFLOAT3(-1.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) },\n        { XMFLOAT3(-1.0f,  1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) },\n        { XMFLOAT3( 1.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) },\n        { XMFLOAT3( 1.0f,  1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) }\n    };\n\n    D3D11_BUFFER_DESC bufferDesc = {};\n    bufferDesc.Usage = D3D11_USAGE_DEFAULT;\n    bufferDesc.ByteWidth = sizeof(vertices);\n    bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;\n\n    D3D11_SUBRESOURCE_DATA initData = {};\n    initData.pSysMem = vertices;\n\n    hr = m_device-&gt;CreateBuffer(&amp;bufferDesc, &amp;initData, &amp;m_vertexBuffer);\n    if (FAILED(hr)) return false;\n\n    return true;\n}\n\n\/\/ \u521b\u5efa\u8bbe\u5907\u548c\u4ea4\u6362\u94fe\nbool D3D11VideoRenderer::CreateDeviceAndSwapChain() {\n    DXGI_SWAP_CHAIN_DESC swapChainDesc = {};\n    swapChainDesc.BufferCount = 2;\n    swapChainDesc.BufferDesc.Width = 0;\n    swapChainDesc.BufferDesc.Height = 0;\n    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\n    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;\n    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;\n    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;\n    swapChainDesc.OutputWindow = m_hwnd;\n    swapChainDesc.SampleDesc.Count = 1;\n    swapChainDesc.SampleDesc.Quality = 0;\n    swapChainDesc.Windowed = TRUE;\n    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;\n\n    D3D_FEATURE_LEVEL featureLevels[] = {\n        D3D_FEATURE_LEVEL_11_1,\n        D3D_FEATURE_LEVEL_11_0\n    };\n\n    HRESULT hr = D3D11CreateDeviceAndSwapChain(\n        nullptr,\n        D3D_DRIVER_TYPE_HARDWARE,\n        nullptr,\n        D3D11_CREATE_DEVICE_BGRA_SUPPORT,\n        featureLevels,\n        ARRAYSIZE(featureLevels),\n        D3D11_SDK_VERSION,\n        &amp;swapChainDesc,\n        &amp;m_swapChain,\n        &amp;m_device,\n        nullptr,\n        &amp;m_context\n    );\n\n    return SUCCEEDED(hr);\n}\n\n\/\/ \u521b\u5efa\u6e32\u67d3\u76ee\u6807\nbool D3D11VideoRenderer::CreateRenderTarget() {\n    ComPtr&lt;ID3D11Texture2D&gt; backBuffer;\n    HRESULT hr = m_swapChain-&gt;GetBuffer(0, __uuidof(ID3D11Texture2D), \n                                        (void**)&amp;backBuffer);\n    if (FAILED(hr)) return false;\n\n    hr = m_device-&gt;CreateRenderTargetView(backBuffer.Get(), nullptr, \n                                          &amp;m_renderTargetView);\n    return SUCCEEDED(hr);\n}\n\n\/\/ \u6e32\u67d3\u5e27\nbool D3D11VideoRenderer::RenderFrame(uint8_t* yuvData, int width, int height) {\n    \/\/ \u5982\u679c\u5c3a\u5bf8\u53d8\u5316\uff0c\u91cd\u65b0\u521b\u5efa\u7eb9\u7406\n    if (width != m_width || height != m_height) {\n        if (!CreateTextures(width, height)) return false;\n        m_width = width;\n        m_height = height;\n    }\n\n    \/\/ \u66f4\u65b0 YUV \u7eb9\u7406\n    int ySize = width * height;\n    int uvSize = (width \/ 2) * (height \/ 2);\n\n    uint8_t* yData = yuvData;\n    uint8_t* uData = yuvData + ySize;\n    uint8_t* vData = yuvData + ySize + uvSize;\n\n    UpdateYUVTextures(yData, uData, vData, width, height);\n\n    \/\/ \u6e05\u7a7a\u6e32\u67d3\u76ee\u6807\n    float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };\n    m_context-&gt;ClearRenderTargetView(m_renderTargetView.Get(), clearColor);\n\n    \/\/ \u8bbe\u7f6e\u6e32\u67d3\u76ee\u6807\n    m_context-&gt;OMSetRenderTargets(1, m_renderTargetView.GetAddressOf(), nullptr);\n\n    \/\/ \u8bbe\u7f6e\u89c6\u53e3\n    RECT clientRect;\n    GetClientRect(m_hwnd, &amp;clientRect);\n    D3D11_VIEWPORT viewport = {};\n    viewport.Width = static_cast&lt;float&gt;(clientRect.right - clientRect.left);\n    viewport.Height = static_cast&lt;float&gt;(clientRect.bottom - clientRect.top);\n    viewport.MinDepth = 0.0f;\n    viewport.MaxDepth = 1.0f;\n    m_context-&gt;RSSetViewports(1, &amp;viewport);\n\n    \/\/ \u8bbe\u7f6e\u7740\u8272\u5668\u548c\u8d44\u6e90\n    m_context-&gt;VSSetShader(m_vertexShader.Get(), nullptr, 0);\n    m_context-&gt;PSSetShader(m_pixelShader.Get(), nullptr, 0);\n\n    ID3D11ShaderResourceView* srvs[] = { \n        m_srvY.Get(), m_srvU.Get(), m_srvV.Get() \n    };\n    m_context-&gt;PSSetShaderResources(0, 3, srvs);\n    m_context-&gt;PSSetSamplers(0, 1, m_sampler.GetAddressOf());\n\n    \/\/ \u8bbe\u7f6e\u9876\u70b9\u7f13\u51b2\u533a\n    UINT stride = sizeof(float) * 5; \/\/ position(3) + texCoord(2)\n    UINT offset = 0;\n    m_context-&gt;IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), \n                                 &amp;stride, &amp;offset);\n    m_context-&gt;IASetInputLayout(m_inputLayout.Get());\n    m_context-&gt;IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);\n\n    \/\/ \u7ed8\u5236\n    m_context-&gt;Draw(4, 0);\n\n    \/\/ \u5448\u73b0\n    m_swapChain-&gt;Present(1, 0); \/\/ VSync\n\n    return true;\n}\n\n\/\/ \u66f4\u65b0 YUV \u7eb9\u7406\nvoid D3D11VideoRenderer::UpdateYUVTextures(uint8_t* yData, uint8_t* uData, \n                                          uint8_t* vData, int width, int height) {\n    \/\/ \u66f4\u65b0 Y \u7eb9\u7406\n    m_context-&gt;UpdateSubresource(m_textureY.Get(), 0, nullptr, \n                                yData, width, 0);\n\n    \/\/ \u66f4\u65b0 U \u7eb9\u7406\n    m_context-&gt;UpdateSubresource(m_textureU.Get(), 0, nullptr, \n                                uData, width \/ 2, 0);\n\n    \/\/ \u66f4\u65b0 V \u7eb9\u7406\n    m_context-&gt;UpdateSubresource(m_textureV.Get(), 0, nullptr, \n                                vData, width \/ 2, 0);\n}\n\n\/\/ \u521b\u5efa\u7eb9\u7406\nbool D3D11VideoRenderer::CreateTextures(int width, int height) {\n    \/\/ \u521b\u5efa Y \u7eb9\u7406\n    D3D11_TEXTURE2D_DESC texDesc = {};\n    texDesc.Width = width;\n    texDesc.Height = height;\n    texDesc.MipLevels = 1;\n    texDesc.ArraySize = 1;\n    texDesc.Format = DXGI_FORMAT_R8_UNORM;\n    texDesc.SampleDesc.Count = 1;\n    texDesc.Usage = D3D11_USAGE_DEFAULT;\n    texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;\n\n    HRESULT hr = m_device-&gt;CreateTexture2D(&amp;texDesc, nullptr, &amp;m_textureY);\n    if (FAILED(hr)) return false;\n\n    hr = m_device-&gt;CreateShaderResourceView(m_textureY.Get(), nullptr, &amp;m_srvY);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa U \u7eb9\u7406\n    texDesc.Width = width \/ 2;\n    texDesc.Height = height \/ 2;\n\n    hr = m_device-&gt;CreateTexture2D(&amp;texDesc, nullptr, &amp;m_textureU);\n    if (FAILED(hr)) return false;\n\n    hr = m_device-&gt;CreateShaderResourceView(m_textureU.Get(), nullptr, &amp;m_srvU);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa V \u7eb9\u7406\n    hr = m_device-&gt;CreateTexture2D(&amp;texDesc, nullptr, &amp;m_textureV);\n    if (FAILED(hr)) return false;\n\n    hr = m_device-&gt;CreateShaderResourceView(m_textureV.Get(), nullptr, &amp;m_srvV);\n    if (FAILED(hr)) return false;\n\n    return true;\n}\n\n\/\/ \u6e05\u7406\u8d44\u6e90\nvoid D3D11VideoRenderer::Cleanup() {\n    if (m_context) {\n        m_context-&gt;ClearState();\n        m_context-&gt;Flush();\n    }\n}\n<\/code><\/pre>\n<h5><span class=\"ez-toc-section\" id=\"22_HLSL_%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81\"><\/span>2.2 HLSL \u7740\u8272\u5668\u4ee3\u7801<span class=\"ez-toc-section-end\"><\/span><\/h5>\n<pre><code class=\"language-hlsl\">\/\/ VertexShader.hlsl\nstruct VS_INPUT {\n    float3 position : POSITION;\n    float2 texCoord : TEXCOORD0;\n};\n\nstruct PS_INPUT {\n    float4 position : SV_POSITION;\n    float2 texCoord : TEXCOORD0;\n};\n\nPS_INPUT main(VS_INPUT input) {\n    PS_INPUT output;\n    output.position = float4(input.position, 1.0f);\n    output.texCoord = input.texCoord;\n    return output;\n}\n<\/code><\/pre>\n<pre><code class=\"language-hlsl\">\/\/ PixelShader.hlsl (YUV to RGB \u8f6c\u6362)\nTexture2D textureY : register(t0);\nTexture2D textureU : register(t1);\nTexture2D textureV : register(t2);\nSamplerState samplerState : register(s0);\n\nstruct PS_INPUT {\n    float4 position : SV_POSITION;\n    float2 texCoord : TEXCOORD0;\n};\n\nfloat4 main(PS_INPUT input) : SV_TARGET {\n    float y = textureY.Sample(samplerState, input.texCoord).r;\n    float u = textureU.Sample(samplerState, input.texCoord).r - 0.5f;\n    float v = textureV.Sample(samplerState, input.texCoord).r - 0.5f;\n\n    \/\/ YUV to RGB \u8f6c\u6362\u77e9\u9635 (BT.709)\n    float r = y + 1.5748f * v;\n    float g = y - 0.1873f * u - 0.4681f * v;\n    float b = y + 1.8556f * u;\n\n    return float4(r, g, b, 1.0f);\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"3_Media_Foundation_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93\"><\/span>3. Media Foundation \u89c6\u9891\u6e32\u67d3<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-cpp\">#include &lt;mfapi.h&gt;\n#include &lt;mfidl.h&gt;\n#include &lt;mfreadwrite.h&gt;\n#include &lt;evr.h&gt;\n\nclass MediaFoundationRenderer {\npublic:\n    MediaFoundationRenderer(HWND hwnd);\n    ~MediaFoundationRenderer();\n\n    bool Initialize();\n    bool LoadVideo(const wchar_t* filePath);\n    bool Play();\n    bool Pause();\n    void Stop();\n\nprivate:\n    HWND m_hwnd;\n\n    ComPtr&lt;IMFMediaSession&gt; m_session;\n    ComPtr&lt;IMFMediaSource&gt; m_mediaSource;\n    ComPtr&lt;IMFTopology&gt; m_topology;\n    ComPtr&lt;IMFVideoDisplayControl&gt; m_videoDisplay;\n};\n\nMediaFoundationRenderer::MediaFoundationRenderer(HWND hwnd) \n    : m_hwnd(hwnd) {\n}\n\nbool MediaFoundationRenderer::Initialize() {\n    \/\/ \u521d\u59cb\u5316 Media Foundation\n    HRESULT hr = MFStartup(MF_VERSION);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa\u5a92\u4f53\u4f1a\u8bdd\n    hr = MFCreateMediaSession(nullptr, &amp;m_session);\n    if (FAILED(hr)) return false;\n\n    return true;\n}\n\nbool MediaFoundationRenderer::LoadVideo(const wchar_t* filePath) {\n    \/\/ \u521b\u5efa\u5a92\u4f53\u6e90\n    ComPtr&lt;IMFSourceResolver&gt; sourceResolver;\n    HRESULT hr = MFCreateSourceResolver(&amp;sourceResolver);\n    if (FAILED(hr)) return false;\n\n    MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;\n    ComPtr&lt;IUnknown&gt; source;\n\n    hr = sourceResolver-&gt;CreateObjectFromURL(\n        filePath,\n        MF_RESOLUTION_MEDIASOURCE,\n        nullptr,\n        &amp;objectType,\n        &amp;source\n    );\n    if (FAILED(hr)) return false;\n\n    hr = source.As(&amp;m_mediaSource);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa\u62d3\u6251\n    hr = MFCreateTopology(&amp;m_topology);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa EVR\uff08Enhanced Video Renderer\uff09\n    ComPtr&lt;IMFActivate&gt; evrActivate;\n    hr = MFCreateVideoRendererActivate(m_hwnd, &amp;evrActivate);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u914d\u7f6e\u62d3\u6251\uff08\u7b80\u5316\u7248\uff0c\u5b9e\u9645\u9700\u8981\u66f4\u590d\u6742\u7684\u914d\u7f6e\uff09\n    \/\/ ... \u6dfb\u52a0\u6e90\u8282\u70b9\u3001\u8f93\u51fa\u8282\u70b9\u7b49\n\n    \/\/ \u8bbe\u7f6e\u62d3\u6251\n    hr = m_session-&gt;SetTopology(0, m_topology.Get());\n    if (FAILED(hr)) return false;\n\n    return true;\n}\n\nbool MediaFoundationRenderer::Play() {\n    PROPVARIANT varStart;\n    PropVariantInit(&amp;varStart);\n\n    HRESULT hr = m_session-&gt;Start(&amp;GUID_NULL, &amp;varStart);\n    PropVariantClear(&amp;varStart);\n\n    return SUCCEEDED(hr);\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"4_Windows_Desktop_Duplication_API%EF%BC%88%E5%B1%8F%E5%B9%95%E6%8D%95%E8%8E%B7%EF%BC%89\"><\/span>4. Windows Desktop Duplication API\uff08\u5c4f\u5e55\u6355\u83b7\uff09<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-cpp\">#include &lt;dxgi1_2.h&gt;\n\nclass DesktopDuplicationRenderer {\npublic:\n    bool Initialize();\n    bool CaptureFrame(ID3D11Texture2D** outTexture);\n\nprivate:\n    ComPtr&lt;ID3D11Device&gt; m_device;\n    ComPtr&lt;ID3D11DeviceContext&gt; m_context;\n    ComPtr&lt;IDXGIOutputDuplication&gt; m_duplication;\n};\n\nbool DesktopDuplicationRenderer::Initialize() {\n    \/\/ \u521b\u5efa D3D11 \u8bbe\u5907\n    D3D_FEATURE_LEVEL featureLevel;\n    HRESULT hr = D3D11CreateDevice(\n        nullptr,\n        D3D_DRIVER_TYPE_HARDWARE,\n        nullptr,\n        0,\n        nullptr,\n        0,\n        D3D11_SDK_VERSION,\n        &amp;m_device,\n        &amp;featureLevel,\n        &amp;m_context\n    );\n    if (FAILED(hr)) return false;\n\n    \/\/ \u83b7\u53d6 DXGI \u8bbe\u5907\n    ComPtr&lt;IDXGIDevice&gt; dxgiDevice;\n    hr = m_device.As(&amp;dxgiDevice);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u83b7\u53d6\u9002\u914d\u5668\n    ComPtr&lt;IDXGIAdapter&gt; dxgiAdapter;\n    hr = dxgiDevice-&gt;GetAdapter(&amp;dxgiAdapter);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u83b7\u53d6\u8f93\u51fa\n    ComPtr&lt;IDXGIOutput&gt; dxgiOutput;\n    hr = dxgiAdapter-&gt;EnumOutputs(0, &amp;dxgiOutput);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u83b7\u53d6 Output1\n    ComPtr&lt;IDXGIOutput1&gt; dxgiOutput1;\n    hr = dxgiOutput.As(&amp;dxgiOutput1);\n    if (FAILED(hr)) return false;\n\n    \/\/ \u521b\u5efa\u684c\u9762\u590d\u5236\n    hr = dxgiOutput1-&gt;DuplicateOutput(m_device.Get(), &amp;m_duplication);\n    if (FAILED(hr)) return false;\n\n    return true;\n}\n\nbool DesktopDuplicationRenderer::CaptureFrame(ID3D11Texture2D** outTexture) {\n    ComPtr&lt;IDXGIResource&gt; desktopResource;\n    DXGI_OUTDUPL_FRAME_INFO frameInfo;\n\n    \/\/ \u83b7\u53d6\u5e27\n    HRESULT hr = m_duplication-&gt;AcquireNextFrame(\n        500,  \/\/ \u8d85\u65f6\u65f6\u95f4\uff08ms\uff09\n        &amp;frameInfo,\n        &amp;desktopResource\n    );\n\n    if (FAILED(hr)) {\n        if (hr == DXGI_ERROR_WAIT_TIMEOUT) {\n            \/\/ \u6ca1\u6709\u65b0\u5e27\n            return false;\n        }\n        return false;\n    }\n\n    \/\/ \u8f6c\u6362\u4e3a\u7eb9\u7406\n    ComPtr&lt;ID3D11Texture2D&gt; texture;\n    hr = desktopResource.As(&amp;texture);\n    if (FAILED(hr)) {\n        m_duplication-&gt;ReleaseFrame();\n        return false;\n    }\n\n    *outTexture = texture.Detach();\n\n    \/\/ \u91ca\u653e\u5e27\n    m_duplication-&gt;ReleaseFrame();\n\n    return true;\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"5_Windows_%E5%B9%B3%E5%8F%B0%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B%E5%9B%BE\"><\/span>5. Windows \u5e73\u53f0\u6e32\u67d3\u6d41\u7a0b\u56fe<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-mermaid\">sequenceDiagram\n    participant App as \u5e94\u7528\u7a0b\u5e8f\n    participant Decoder as \u89e3\u7801\u5668\n    participant D3D11 as D3D11\u6e32\u67d3\u5668\n    participant GPU as GPU\n    participant Display as \u663e\u793a\u5668\n\n    App-&gt;&gt;Decoder: \u53d1\u9001\u89c6\u9891\u6570\u636e\n    Decoder-&gt;&gt;Decoder: \u89e3\u7801\u89c6\u9891\u5e27\n    Decoder-&gt;&gt;D3D11: \u4f20\u9012YUV\u6570\u636e\n\n    D3D11-&gt;&gt;D3D11: CreateTextures(YUV)\n    D3D11-&gt;&gt;D3D11: UpdateSubresource\n\n    D3D11-&gt;&gt;GPU: \u8bbe\u7f6e\u6e32\u67d3\u7ba1\u7ebf\n    D3D11-&gt;&gt;GPU: \u7ed1\u5b9a\u7eb9\u7406\u548c\u7740\u8272\u5668\n    D3D11-&gt;&gt;GPU: Draw\u8c03\u7528\n\n    GPU-&gt;&gt;GPU: YUV to RGB\u8f6c\u6362\n    GPU-&gt;&gt;GPU: \u6e32\u67d3\u5230\u540e\u5907\u7f13\u51b2\u533a\n\n    D3D11-&gt;&gt;Display: SwapChain-&gt;Present()\n    Display-&gt;&gt;Display: \u663e\u793a\u753b\u9762\n\n    Note over D3D11,Display: VSync\u540c\u6b65\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"6_WPF_%E8%A7%86%E9%A2%91%E6%B8%B2%E6%9F%93\"><\/span>6. WPF \u89c6\u9891\u6e32\u67d3<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-csharp\">using System;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\n\npublic class WpfVideoRenderer {\n    private Image imageControl;\n    private WriteableBitmap bitmap;\n\n    public WpfVideoRenderer(Image image) {\n        imageControl = image;\n    }\n\n    public void Initialize(int width, int height) {\n        \/\/ \u521b\u5efa WriteableBitmap\n        bitmap = new WriteableBitmap(\n            width, \n            height, \n            96, 96, \n            PixelFormats.Bgra32, \n            null\n        );\n\n        imageControl.Source = bitmap;\n    }\n\n    public void RenderFrame(byte[] rgbData, int width, int height) {\n        if (bitmap == null || \n            bitmap.PixelWidth != width || \n            bitmap.PixelHeight != height) {\n            Initialize(width, height);\n        }\n\n        \/\/ \u66f4\u65b0\u4f4d\u56fe\n        bitmap.Lock();\n\n        try {\n            \/\/ \u590d\u5236\u6570\u636e\u5230\u540e\u5907\u7f13\u51b2\u533a\n            int stride = width * 4; \/\/ BGRA32\n            Int32Rect rect = new Int32Rect(0, 0, width, height);\n\n            bitmap.WritePixels(rect, rgbData, stride, 0);\n        }\n        finally {\n            bitmap.Unlock();\n        }\n\n        \/\/ \u6807\u8bb0\u4e3a\u810f\u533a\u57df\uff0c\u89e6\u53d1\u91cd\u7ed8\n        bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));\n    }\n\n    \/\/ \u4ece YUV \u8f6c\u6362\u5e76\u6e32\u67d3\n    public void RenderYUVFrame(byte[] yuvData, int width, int height) {\n        byte[] rgbData = ConvertYUVToRGB(yuvData, width, height);\n        RenderFrame(rgbData, width, height);\n    }\n\n    private byte[] ConvertYUVToRGB(byte[] yuv, int width, int height) {\n        \/\/ YUV to RGB \u8f6c\u6362\u5b9e\u73b0\n        byte[] rgb = new byte[width * height * 4];\n\n        int ySize = width * height;\n        int uvSize = ySize \/ 4;\n\n        for (int i = 0; i &lt; height; i++) {\n            for (int j = 0; j &lt; width; j++) {\n                int yIndex = i * width + j;\n                int uvIndex = (i \/ 2) * (width \/ 2) + (j \/ 2);\n\n                float y = yuv[yIndex];\n                float u = yuv[ySize + uvIndex] - 128;\n                float v = yuv[ySize + uvSize + uvIndex] - 128;\n\n                \/\/ BT.709 \u8f6c\u6362\n                int r = (int)(y + 1.5748f * v);\n                int g = (int)(y - 0.1873f * u - 0.4681f * v);\n                int b = (int)(y + 1.8556f * u);\n\n                \/\/ \u9650\u5236\u8303\u56f4\n                r = Math.Max(0, Math.Min(255, r));\n                g = Math.Max(0, Math.Min(255, g));\n                b = Math.Max(0, Math.Min(255, b));\n\n                int rgbIndex = (i * width + j) * 4;\n                rgb[rgbIndex] = (byte)b;     \/\/ B\n                rgb[rgbIndex + 1] = (byte)g; \/\/ G\n                rgb[rgbIndex + 2] = (byte)r; \/\/ R\n                rgb[rgbIndex + 3] = 255;     \/\/ A\n            }\n        }\n\n        return rgb;\n    }\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"7_Windows_%E7%94%BB%E4%B8%AD%E7%94%BB%E5%AE%9E%E7%8E%B0%EF%BC%88Compact_Overlay%EF%BC%89\"><\/span>7. Windows \u753b\u4e2d\u753b\u5b9e\u73b0\uff08Compact Overlay\uff09<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-csharp\">using Windows.UI.ViewManagement;\nusing Windows.Foundation;\n\npublic class WindowsPipController {\n    private ApplicationView appView;\n\n    public async Task&lt;bool&gt; EnterCompactOverlay() {\n        appView = ApplicationView.GetForCurrentView();\n\n        \/\/ \u68c0\u67e5\u662f\u5426\u652f\u6301 Compact Overlay\n        if (!appView.IsViewModeSupported(ApplicationViewMode.CompactOverlay)) {\n            return false;\n        }\n\n        \/\/ \u8bbe\u7f6e\u9996\u9009\u5927\u5c0f\n        var preferences = ViewModePreferences.CreateDefault(\n            ApplicationViewMode.CompactOverlay\n        );\n        preferences.CustomSize = new Size(400, 300);\n\n        \/\/ \u8fdb\u5165 Compact Overlay \u6a21\u5f0f\n        bool success = await appView.TryEnterViewModeAsync(\n            ApplicationViewMode.CompactOverlay,\n            preferences\n        );\n\n        return success;\n    }\n\n    public async Task&lt;bool&gt; ExitCompactOverlay() {\n        if (appView == null) return false;\n\n        \/\/ \u9000\u51fa Compact Overlay\uff0c\u8fd4\u56de\u6b63\u5e38\u6a21\u5f0f\n        bool success = await appView.TryEnterViewModeAsync(\n            ApplicationViewMode.Default\n        );\n\n        return success;\n    }\n\n    \/\/ \u4e8b\u4ef6\u5904\u7406\n    public void Initialize() {\n        appView = ApplicationView.GetForCurrentView();\n\n        \/\/ \u76d1\u542c\u7a97\u53e3\u5927\u5c0f\u53d8\u5316\n        appView.VisibleBoundsChanged += (sender, args) =&gt; {\n            \/\/ \u5904\u7406\u7a97\u53e3\u5927\u5c0f\u53d8\u5316\n            if (appView.ViewMode == ApplicationViewMode.CompactOverlay) {\n                \/\/ \u753b\u4e2d\u753b\u6a21\u5f0f\u4e0b\u7684\u5904\u7406\n                OnCompactOverlayModeChanged(true);\n            } else {\n                OnCompactOverlayModeChanged(false);\n            }\n        };\n    }\n\n    private void OnCompactOverlayModeChanged(bool isCompactOverlay) {\n        \/\/ \u6839\u636e\u6a21\u5f0f\u8c03\u6574 UI\n        if (isCompactOverlay) {\n            \/\/ \u9690\u85cf\u590d\u6742\u63a7\u4ef6\uff0c\u53ea\u663e\u793a\u89c6\u9891\n        } else {\n            \/\/ \u663e\u793a\u5b8c\u6574 UI\n        }\n    }\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"8_Windows_%E6%B8%B2%E6%9F%93%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96\"><\/span>8. Windows \u6e32\u67d3\u6027\u80fd\u4f18\u5316<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-cpp\">class RenderOptimizer {\npublic:\n    \/\/ 1. \u4f7f\u7528\u786c\u4ef6\u52a0\u901f\u89c6\u9891\u89e3\u7801\n    bool EnableHardwareDecoding() {\n        ComPtr&lt;ID3D11VideoDevice&gt; videoDevice;\n        HRESULT hr = m_device.As(&amp;videoDevice);\n        if (FAILED(hr)) return false;\n\n        \/\/ \u521b\u5efa\u89c6\u9891\u89e3\u7801\u5668\n        D3D11_VIDEO_DECODER_DESC decoderDesc = {};\n        decoderDesc.Guid = D3D11_DECODER_PROFILE_H264_VLD_NOFGT;\n        decoderDesc.SampleWidth = 1920;\n        decoderDesc.SampleHeight = 1080;\n        decoderDesc.OutputFormat = DXGI_FORMAT_NV12;\n\n        ComPtr&lt;ID3D11VideoDecoder&gt; decoder;\n        hr = videoDevice-&gt;CreateVideoDecoder(&amp;decoderDesc, nullptr, &amp;decoder);\n\n        return SUCCEEDED(hr);\n    }\n\n    \/\/ 2. \u4f7f\u7528 Flip \u6a21\u578b\u51cf\u5c11\u5ef6\u8fdf\n    void EnableFlipModel() {\n        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};\n        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;\n        swapChainDesc.BufferCount = 2;\n        swapChainDesc.Scaling = DXGI_SCALING_STRETCH;\n        swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;\n        \/\/ ... \u5176\u4ed6\u914d\u7f6e\n    }\n\n    \/\/ 3. \u5f02\u6b65\u6e32\u67d3\n    std::thread renderThread;\n    std::queue&lt;VideoFrame&gt; frameQueue;\n    std::mutex queueMutex;\n    std::condition_variable queueCV;\n    bool isRunning = true;\n\n    void StartRenderThread() {\n        renderThread = std::thread([this]() {\n            while (isRunning) {\n                VideoFrame frame;\n\n                {\n                    std::unique_lock&lt;std::mutex&gt; lock(queueMutex);\n                    queueCV.wait(lock, [this] { \n                        return !frameQueue.empty() || !isRunning; \n                    });\n\n                    if (!isRunning) break;\n\n                    frame = frameQueue.front();\n                    frameQueue.pop();\n                }\n\n                \/\/ \u6e32\u67d3\u5e27\n                RenderFrame(frame);\n            }\n        });\n    }\n\n    \/\/ 4. \u7eb9\u7406\u6c60\u7ba1\u7406\n    class TexturePool {\n    public:\n        ComPtr&lt;ID3D11Texture2D&gt; AcquireTexture(int width, int height) {\n            std::lock_guard&lt;std::mutex&gt; lock(mutex_);\n\n            for (auto&amp; tex : textures_) {\n                if (!tex.inUse &amp;&amp; tex.width == width &amp;&amp; tex.height == height) {\n                    tex.inUse = true;\n                    return tex.texture;\n                }\n            }\n\n            \/\/ \u521b\u5efa\u65b0\u7eb9\u7406\n            TextureInfo info;\n            info.width = width;\n            info.height = height;\n            info.inUse = true;\n\n            \/\/ \u521b\u5efa D3D11 \u7eb9\u7406...\n\n            textures_.push_back(info);\n            return info.texture;\n        }\n\n        void ReleaseTexture(ComPtr&lt;ID3D11Texture2D&gt; texture) {\n            std::lock_guard&lt;std::mutex&gt; lock(mutex_);\n\n            for (auto&amp; tex : textures_) {\n                if (tex.texture == texture) {\n                    tex.inUse = false;\n                    break;\n                }\n            }\n        }\n\n    private:\n        struct TextureInfo {\n            ComPtr&lt;ID3D11Texture2D&gt; texture;\n            int width;\n            int height;\n            bool inUse;\n        };\n\n        std::vector&lt;TextureInfo&gt; textures_;\n        std::mutex mutex_;\n    };\n};\n<\/code><\/pre>\n<h3><span class=\"ez-toc-section\" id=\"%E6%B8%B2%E6%9F%93%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94\"><\/span>\u6e32\u67d3\u6027\u80fd\u5bf9\u6bd4<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<table>\n<thead>\n<tr>\n<th>\u6e32\u67d3\u65b9\u5f0f<\/th>\n<th>\u5e73\u53f0<\/th>\n<th>CPU\u5360\u7528<\/th>\n<th>GPU\u5360\u7528<\/th>\n<th>\u5ef6\u8fdf<\/th>\n<th>\u590d\u6742\u5ea6<\/th>\n<th>\u9002\u7528\u573a\u666f<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>AVPlayerLayer<\/td>\n<td>iOS\/macOS<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u666e\u901a\u89c6\u9891\u64ad\u653e<\/td>\n<\/tr>\n<tr>\n<td>AVSampleBufferDisplayLayer<\/td>\n<td>iOS\/macOS<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4e2d<\/td>\n<td>\u81ea\u5b9a\u4e49\u89e3\u7801+PiP<\/td>\n<\/tr>\n<tr>\n<td>Metal<\/td>\n<td>iOS\/macOS<\/td>\n<td>\u4f4e<\/td>\n<td>\u4e2d<\/td>\n<td>\u6781\u4f4e<\/td>\n<td>\u9ad8<\/td>\n<td>\u9ad8\u6027\u80fd\u3001\u7279\u6548\u6e32\u67d3<\/td>\n<\/tr>\n<tr>\n<td>OpenGL ES<\/td>\n<td>iOS\/Android<\/td>\n<td>\u4e2d<\/td>\n<td>\u4e2d<\/td>\n<td>\u4f4e<\/td>\n<td>\u4e2d<\/td>\n<td>\u8de8\u5e73\u53f0\u3001\u901a\u7528\u6e32\u67d3<\/td>\n<\/tr>\n<tr>\n<td>SurfaceView<\/td>\n<td>Android<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u6807\u51c6\u89c6\u9891\u64ad\u653e<\/td>\n<\/tr>\n<tr>\n<td>TextureView<\/td>\n<td>Android<\/td>\n<td>\u4e2d<\/td>\n<td>\u4e2d<\/td>\n<td>\u4e2d<\/td>\n<td>\u4e2d<\/td>\n<td>\u9700\u8981\u52a8\u753b\/\u53d8\u6362<\/td>\n<\/tr>\n<tr>\n<td>Direct3D 11<\/td>\n<td>Windows<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4e2d<\/td>\n<td>Windows\u4e3b\u6d41\u6e32\u67d3<\/td>\n<\/tr>\n<tr>\n<td>Direct3D 12<\/td>\n<td>Windows<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u6781\u4f4e<\/td>\n<td>\u9ad8<\/td>\n<td>\u9ad8\u6027\u80fd\u6e38\u620f\/\u4e13\u4e1a\u5e94\u7528<\/td>\n<\/tr>\n<tr>\n<td>Media Foundation<\/td>\n<td>Windows<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>\u4f4e<\/td>\n<td>Windows\u5a92\u4f53\u64ad\u653e<\/td>\n<\/tr>\n<tr>\n<td>WPF WriteableBitmap<\/td>\n<td>Windows<\/td>\n<td>\u9ad8<\/td>\n<td>\u4f4e<\/td>\n<td>\u4e2d<\/td>\n<td>\u4f4e<\/td>\n<td>WPF\u5e94\u7528\u96c6\u6210<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3><span class=\"ez-toc-section\" id=\"Windows_vs_iOS_vs_Android_%E6%B8%B2%E6%9F%93%E5%AF%B9%E6%AF%94\"><\/span>Windows vs iOS vs Android \u6e32\u67d3\u5bf9\u6bd4<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<pre><code class=\"language-mermaid\">graph TD\n    subgraph Windows[&quot;Windows \u6e32\u67d3\u65b9\u6848&quot;]\n        WinD3D11[&quot;Direct3D 11&lt;br\/&gt;\u2713 \u786c\u4ef6\u52a0\u901f&lt;br\/&gt;\u2713 \u4e3b\u6d41\u65b9\u6848&lt;br\/&gt;\u2713 \u6613\u7528\u6027\u9ad8&quot;]\n        WinD3D12[&quot;Direct3D 12&lt;br\/&gt;\u2713 \u6781\u81f4\u6027\u80fd&lt;br\/&gt;\u2713 \u4f4e\u5ef6\u8fdf&lt;br\/&gt;\u26a0\ufe0f \u590d\u6742\u5ea6\u9ad8&quot;]\n        WinMF[&quot;Media Foundation&lt;br\/&gt;\u2713 \u7cfb\u7edf\u96c6\u6210&lt;br\/&gt;\u2713 \u7f16\u89e3\u7801\u4e00\u4f53&lt;br\/&gt;\u2713 \u5f00\u7bb1\u5373\u7528&quot;]\n        WinWPF[&quot;WPF&lt;br\/&gt;\u2713 UI\u96c6\u6210\u597d&lt;br\/&gt;\u26a0\ufe0f \u6027\u80fd\u4e00\u822c&lt;br\/&gt;\u2713 \u5f00\u53d1\u6548\u7387\u9ad8&quot;]\n    end\n\n    subgraph iOS[&quot;iOS \u6e32\u67d3\u65b9\u6848&quot;]\n        iOSAVP[&quot;AVPlayer&lt;br\/&gt;\u2713 \u7b80\u5355\u6613\u7528&lt;br\/&gt;\u2713 \u7cfb\u7edf\u4f18\u5316&lt;br\/&gt;\u2713 \u81ea\u52a8PiP&quot;]\n        iOSAVS[&quot;AVSampleBuffer&lt;br\/&gt;\u2713 \u81ea\u5b9a\u4e49\u89e3\u7801&lt;br\/&gt;\u2713 \u5b9e\u65f6\u6d41&lt;br\/&gt;\u2713 PiP\u652f\u6301&quot;]\n        iOSMetal[&quot;Metal&lt;br\/&gt;\u2713 \u6781\u81f4\u6027\u80fd&lt;br\/&gt;\u2713 \u4f4e\u529f\u8017&lt;br\/&gt;\u26a0\ufe0f \u590d\u6742\u5ea6\u9ad8&quot;]\n    end\n\n    subgraph Android[&quot;Android \u6e32\u67d3\u65b9\u6848&quot;]\n        AndSurface[&quot;SurfaceView&lt;br\/&gt;\u2713 \u6807\u51c6\u65b9\u6848&lt;br\/&gt;\u2713 \u6027\u80fd\u597d&lt;br\/&gt;\u26a0\ufe0f \u52a8\u753b\u652f\u6301\u5f31&quot;]\n        AndTexture[&quot;TextureView&lt;br\/&gt;\u2713 \u7075\u6d3b\u6027\u9ad8&lt;br\/&gt;\u2713 \u652f\u6301\u52a8\u753b&lt;br\/&gt;\u26a0\ufe0f \u5ef6\u8fdf\u7a0d\u9ad8&quot;]\n        AndGL[&quot;OpenGL ES&lt;br\/&gt;\u2713 \u8de8\u5e73\u53f0&lt;br\/&gt;\u2713 \u7279\u6548\u4e30\u5bcc&lt;br\/&gt;\u26a0\ufe0f \u9700\u624b\u52a8\u7ba1\u7406&quot;]\n    end\n\n    style WinD3D11 fill:#0078d4,stroke:#005a9e,stroke-width:2px,color:#fff\n    style iOSMetal fill:#9b59b6,stroke:#8e44ad,stroke-width:2px,color:#fff\n    style AndSurface fill:#4CAF50,stroke:#388E3C,stroke-width:2px,color:#fff\n<\/code><\/pre>\n<p>| AVSampleBufferDisplayLayer | iOS\/macOS | \u4f4e | \u4f4e | \u4f4e | \u4e2d | \u81ea\u5b9a\u4e49\u89e3\u7801+PiP |<br \/>\n| Metal | iOS\/macOS | \u4f4e | \u4e2d | \u6781\u4f4e | \u9ad8 | \u9ad8\u6027\u80fd\u3001\u7279\u6548\u6e32\u67d3 |<br \/>\n| OpenGL ES | iOS\/Android | \u4e2d | \u4e2d | \u4f4e | \u4e2d | \u8de8\u5e73\u53f0\u3001\u901a\u7528\u6e32\u67d3 |<br \/>\n| SurfaceView | Android | \u4f4e | \u4f4e | \u4f4e | \u4f4e | \u6807\u51c6\u89c6\u9891\u64ad\u653e |<br \/>\n| TextureView | Android | \u4e2d | \u4e2d | \u4e2d | \u4e2d | \u9700\u8981\u52a8\u753b\/\u53d8\u6362 |<\/p>\n<h3><span class=\"ez-toc-section\" id=\"%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5\"><\/span>\u6700\u4f73\u5b9e\u8df5<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<h4><span class=\"ez-toc-section\" id=\"1_%E9%80%89%E6%8B%A9%E5%90%88%E9%80%82%E7%9A%84%E6%B8%B2%E6%9F%93%E6%96%B9%E5%BC%8F\"><\/span>1. \u9009\u62e9\u5408\u9002\u7684\u6e32\u67d3\u65b9\u5f0f<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<ul>\n<li><strong>iOS PiP<\/strong>\uff1a\u4f18\u5148\u4f7f\u7528 <code>AVSampleBufferDisplayLayer<\/code>\uff0c\u652f\u6301\u81ea\u5b9a\u4e49\u89e3\u7801<\/li>\n<li><strong>\u9ad8\u6027\u80fd\u573a\u666f<\/strong>\uff1a\u4f7f\u7528 Metal\uff08iOS\uff09\u6216 Vulkan\uff08Android\uff09<\/li>\n<li><strong>\u8de8\u5e73\u53f0\u65b9\u6848<\/strong>\uff1a\u4f7f\u7528 OpenGL ES<\/li>\n<\/ul>\n<h4><span class=\"ez-toc-section\" id=\"2_%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96\"><\/span>2. \u6027\u80fd\u4f18\u5316<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-swift\">\/\/ \u4f18\u5316\u5efa\u8bae\nclass RenderOptimization {\n\n    \/\/ 1. \u590d\u7528\u7eb9\u7406\u5bf9\u8c61\n    private var textureCache: CVMetalTextureCache?\n\n    \/\/ 2. \u4f7f\u7528\u5bf9\u8c61\u6c60\u51cf\u5c11\u5185\u5b58\u5206\u914d\n    private var bufferPool: [CMSampleBuffer] = []\n\n    \/\/ 3. \u63a7\u5236\u6e32\u67d3\u5e27\u7387\n    private let targetFPS = 30\n    private var lastRenderTime: CFTimeInterval = 0\n\n    func shouldRenderFrame() -&gt; Bool {\n        let currentTime = CACurrentMediaTime()\n        let elapsed = currentTime - lastRenderTime\n        let frameInterval = 1.0 \/ Double(targetFPS)\n\n        if elapsed &gt;= frameInterval {\n            lastRenderTime = currentTime\n            return true\n        }\n        return false\n    }\n\n    \/\/ 4. \u5f02\u6b65\u6e32\u67d3\n    private let renderQueue = DispatchQueue(label: &quot;com.video.render&quot;, qos: .userInteractive)\n\n    func renderAsync(_ buffer: CMSampleBuffer) {\n        renderQueue.async { [weak self] in\n            self?.doRender(buffer)\n        }\n    }\n\n    private func doRender(_ buffer: CMSampleBuffer) {\n        \/\/ \u6e32\u67d3\u5b9e\u73b0\n    }\n}\n<\/code><\/pre>\n<h4><span class=\"ez-toc-section\" id=\"3_%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86\"><\/span>3. \u5185\u5b58\u7ba1\u7406<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<pre><code class=\"language-swift\">class MemoryManagement {\n\n    \/\/ \u53ca\u65f6\u91ca\u653e\u4e0d\u7528\u7684\u8d44\u6e90\n    func cleanup() {\n        \/\/ \u6e05\u7406\u7eb9\u7406\u7f13\u5b58\n        CVMetalTextureCacheFlush(textureCache, 0)\n\n        \/\/ \u91ca\u653e\u547d\u4ee4\u7f13\u51b2\u533a\n        commandBuffer = nil\n\n        \/\/ \u6e05\u7a7a\u7f13\u51b2\u6c60\n        bufferPool.removeAll()\n    }\n\n    \/\/ \u76d1\u542c\u5185\u5b58\u8b66\u544a\n    func setupMemoryWarning() {\n        NotificationCenter.default.addObserver(\n            self,\n            selector: #selector(handleMemoryWarning),\n            name: UIApplication.didReceiveMemoryWarningNotification,\n            object: nil\n        )\n    }\n\n    @objc private func handleMemoryWarning() {\n        cleanup()\n    }\n}\n<\/code><\/pre>\n<h3><span class=\"ez-toc-section\" id=\"%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5\"><\/span>\u6545\u969c\u6392\u67e5<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<table>\n<thead>\n<tr>\n<th>\u95ee\u9898<\/th>\n<th>\u53ef\u80fd\u539f\u56e0<\/th>\n<th>\u89e3\u51b3\u65b9\u6848<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>\u753b\u4e2d\u753b\u65e0\u6cd5\u542f\u52a8<\/td>\n<td>\u6743\u9650\u672a\u914d\u7f6e<\/td>\n<td>\u68c0\u67e5 Info.plist \u548c Background Modes<\/td>\n<\/tr>\n<tr>\n<td>\u753b\u9762\u6495\u88c2<\/td>\n<td>\u672a\u542f\u7528 VSync<\/td>\n<td>\u542f\u7528\u5782\u76f4\u540c\u6b65<\/td>\n<\/tr>\n<tr>\n<td>\u753b\u9762\u5361\u987f<\/td>\n<td>\u4e3b\u7ebf\u7a0b\u963b\u585e<\/td>\n<td>\u5f02\u6b65\u6e32\u67d3\uff0c\u4f7f\u7528\u72ec\u7acb\u6e32\u67d3\u961f\u5217<\/td>\n<\/tr>\n<tr>\n<td>\u5185\u5b58\u98d9\u5347<\/td>\n<td>\u7eb9\u7406\u672a\u91ca\u653e<\/td>\n<td>\u53ca\u65f6\u91ca\u653e\u7eb9\u7406\uff0c\u4f7f\u7528\u7eb9\u7406\u7f13\u5b58<\/td>\n<\/tr>\n<tr>\n<td>\u9ed1\u5c4f<\/td>\n<td>\u89e3\u7801\u683c\u5f0f\u4e0d\u652f\u6301<\/td>\n<td>\u68c0\u67e5\u50cf\u7d20\u683c\u5f0f\uff0c\u4f7f\u7528\u6b63\u786e\u7684\u7eb9\u7406\u683c\u5f0f<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>Video Rendering \u6982\u8ff0 \u89c6\u9891\u6e32\u67d3\u5c06\u89e3\u7801\u540e\u7684\u89c6\u9891\u5e27\u6570\u636e\u8f93\u51fa\u5230\u663e\u793a\u8bbe\u5907\uff08\u5c4f\u5e55\uff09\u8fdb\u884c\u663e\u793a\u3002\u672c\u6587\u6863\u8be6\u7ec6\u4ecb [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28],"tags":[],"blocksy_meta":[],"featured_image_urls":{"full":"","thumbnail":"","medium":"","medium_large":"","large":"","1536x1536":"","2048x2048":""},"post_excerpt_stackable":"<p>\u76ee\u5f55 Toggle Video Rendering\u6982\u8ff0\u6e32\u67d3\u65b9\u5f0fOpenGL\/OpenGL ESDirectX\/Vulkan\u5e73\u53f0\u539f\u751f\u6e32\u67d3\u6e32\u67d3\u4f18\u5316\u591a\u7ebf\u7a0b\u6e32\u67d3\u5e27\u540c\u6b65\uff08VSync\uff09\u753b\u9762\u7f29\u653e\u4e0e\u88c1\u526a\u64ad\u653e\u63a7\u5236\u64ad\u653e\/&hellip;<\/p>\n","category_list":"<a href=\"https:\/\/www.v.ixriver.com\/?cat=28\" rel=\"category\">\u89c6\u9891<\/a>","author_info":{"name":"admin","url":"https:\/\/www.v.ixriver.com\/?author=1"},"comments_num":"0 comments","_links":{"self":[{"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=\/wp\/v2\/posts\/791"}],"collection":[{"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=791"}],"version-history":[{"count":0,"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=\/wp\/v2\/posts\/791\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=791"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=791"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.v.ixriver.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}