Closed annetube closed 3 years ago
@annetube the license_key looks fine for me, pls. enable libCurl component debugging in kodi settings and try to figure out where the difference is. The request you posted from chrome is a server-certificate request, if the payload of the first widevine request inside kodi is not 2 byte, then this could be the reason for the precondition failure.
To force the server certificate you could also try to set this flag: inputstream.adaptive.license_flags='persistent_storage'
@peak3d Thanks for your your quick answer, unfortunately I dont have kodi environement to debug, I made and android app, who use Exoplayer. I would like be able to play the mpd, and so far the only solution I saw. Its the solution made by catchupTV team . I asked them about this too, and they told me, they dont really know how, they just send the information above to your plugin.
To be honnest, I'm not really confortable with the terms and dataflow for DRM stream ... Is it enought to get a reponse from server certificate to be able to play it stream ? Knowing Exoplayers needs apparently these kind of informations to play DRM :
{ "name": "TEST DRM", "uri": "URI_OF_VIDEO", "drm_scheme": "widevine", "drm_license_url": "URL_LICENSE", "drm_key_request_properties": { "Authorization" : "CORRECT_TOKEN" } }
And idealy, if you could try, when you've a bit time to see what's on the payload when your plugin make this "server-certificate request".
Thanks for your time
There are 2 communication ways: 1.) simple: no server certificate, payload is the key request (for decryption) 2.) with server certificate: first server certificate is requested (with the 2 byte payload, in general b0801 IIRC), response passed to DRM and after that the key request -> there are 2 requests before media can be decrypted.
You don't have to deal with the content of the payload, its meant as opaque and you send things returned from DRM calls to license server and you feed the DRM with the response of the server. In case of success you can decrypt media, at least for a limited time.
But: you should be able to send at least the 2 byte payload to the licenseserver without having a failure (even you won't have to pass hardcoded 2 bytes in later versions because the 2 bytes come from DRM call.
If you still have your curl errors either your payload is wrong (compare in Chrome if it's really b0801, or something is wrong with your headers or token.
Thank you very much, it works the 2 bytes were "BS EOT" , i tought i tried it but apperently i didnt , anyway again thanks without you i probably gave up
@peak3d Sorry to bother you again, and for the long post comming ... i tought getting these reponse was enough but it doesnt work on my player, you was right the type of response is : "SERVICE_CERTIFICATE"
{status: "OK",…} client_info: [] content_owner: "castlabs" content_provider: "castlabs" internal_status: 139 license: "CAUSxwUKwQIIAxIQFwW5F8wSBIaLBjM6L3cqjBiCtIKSBSKOAjCCAQoCggEBAJntWzsyfateJO/DtiqVtZhSCtW8yzdQPgZFuBTYdrjfQFEEQa2M462xG7iMTnJaXkqeB5UpHVhYQCOn4a8OOKkSeTkwCGELbxWMh4x+Ib/7/up34QGeHleB6KRfRiY9FOYOgFioYHrc4E+shFexN6jWfM3rM3BdmDoh+07svUoQykdJDKR+ql1DghjduvHK3jOS8T1v+2RC/THhv0CwxgTRxLpMlSCkv5fuvWCSmvzu9Vu69WTi0Ods18Vcc6CCuZYSC4NZ7c4kcHCCaA1vZ8bYLErF8xNEkKdO7DevSy8BDFnoKEPiWC8La59dsPxebt9k+9MItHEbzxJQAZyfWgkCAwEAAToUbGljZW5zZS53aWRldmluZS5jb20SgAOuNHMUtag1KX8nE4j7e7jLUnfSSYI83dHaMLkzOVEes8y96gS5RLknwSE0bv296snUE5F+bsF2oQQ4RgpQO8GVK5uk5M4PxL/CCpgIqq9L/NGcHc/N9XTMrCjRtBBBbPneiAQwHL2zNMr80NQJeEI6ZC5UYT3wr8+WykqSSdhV5Cs6cD7xdn9qm9Nta/gr52u/DLpP3lnSq8x2/rZCR7hcQx+8pSJmthn8NpeVQ/ypy727+voOGlXnVaPHvOZV+WRvWCq5z3CqCLl5+Gf2Ogsrf9s2LFvE7NVV2FvKqcWTw4PIV9Sdqrd+QLeFHd/SSZiAjjWyWOddeOrAyhb3BHMEwg2T7eTo/xxvF+YkPj89qPwXCYcOxF+6gjomPwzvofcJOxkJkoMmMzcFBDopvab5tDQsyN9UPLGhGC98X/8z8QSQ+spbJTYLdgFenFoGq47gLwDS6NWYYQSqzE3Udf2W7pzk4ybyG4PHBYV3s4cyzdq8amvtE/sNSdOKReuHpfQ=" message_type: "SERVICE_CERTIFICATE" status: "OK" supported_tracks: []
On the http flow, chrome makes an other "SERVICE_CERTIFICATE" request and got the same response and after these 2 requests it makes 2 another one (apparently called "LICENSE" from the message type) { : and get these responses (same structure but values are a bit different) :
{status: "OK",…} client_info: [{name: "architecture_name", value: "x86-64"}, {name: "company_name", value: "Google"},…] client_max_hdcp_version: "HDCP_V1" content_owner: "castlabs" content_provider: "castlabs" device_state: "RELEASED" device_whitelist_state: "DEVICE_NOT_WHITELISTED" drm_cert_serial_number: "ZWEyZTY5OGQ5NWIyODQ0MTQyMmY0OTk3ZDZmZmQ1NDUwYQ==" internal_status: 0 license: "CAISnwMKKgoQI8ih0fnhHAH8oYaOavtHshIQI8ih0fnhHAH8oYaOavtHshoAIAEoABIMCAEQARgAIAAoAGABGmYSEBieExuXuyR+DFv6nmLYB1kaUFxsgJQ5aggflr9yZEk1FykdiDx74onkgXTM0z2XNbVfg8B+bFXgLyu4FJ43JDZE7yWsJMPq9FkMnAFnmRr56otmlyWaGEXhFkNjUxYEXuRBIAEaUQoQkdlGe9eVMYugfEhn/0OB5xIQLW+C6DHrlWEzvlXvT00tYhogmX3hAuE/j4yrQ23hC1i39RrFX/EbCL7nzs3Ksad8pFEgAigBYgVBVURJTxpOChCTS8fGoCk9W54mGnMkd3UDEhAoGioa3ZcWRwZ0K+LjBnJmGiBBIRSwcRoTNeOTP1FK0DJ6tnH05RhnfefORZ7hvaqTkiACKAFiAlNEGk4KEDmhLCtAmDD+qC9mpdb3vacSEMSLi29JXaA4Y0Arx0H6m2caIBr43YFKtoV0ySHC/sCHRwECak5BVqt+4NdO/elpxvxJIAIoAWICSEQgl52B6AU4AFAFGiA8hDZ0ZsDJyYlfmAaVDSDkjq4Op8pGwU24eSbOTdcw5SKAAgxNh4UGhygQUMTEy1a0at6HNmtkaXuaoa3qU3QsxjuScuhb2KBoH+h0nbvh+sI3hhu4IKABL9OJTDHuoQHeuxp+jQ8WWbUrA2da/C8qcIceQfEiazSb8cfgt70e3zyy1FbnQ4lO8Zj2YkIE6JiOIxgoO7hZUJ/g9nGI5GxKfuLVdQBDdIqeUAXxPYKRosnBeR6A+LqKifqK+26s7N+ALLQ3f6rPWishRqDYohT6WYwfJbK8FsAVGANx8UI3OGlZpKpOfvaoU/4S75drljNkyv8/0qgR6o2umb5YluePll6/XN4/kHEAjsdbDpABHwJ7AhsUa3Xr2ydZA6Ol7+tTcsg=" license_metadata: {,…} content_id: "CmMKTQgBEhCR2UZ715Uxi6B8SGf/Q4HnGghjYXN0bGFicyIkZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09MgdkZWZhdWx0EAEaECPIodH54RwB/KGGjmr7R7I=" license_type: "STREAMING" request_type: "NEW" make: "Google" message_type: "LICENSE" model: "ChromeCDM-Windows-2" platform: "chrome" platform_verification_status: "PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED" pssh_data: {key_id: ["kdlGe9eVMYugfEhn/0OB5w=="], content_id: "ZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09"} content_id: "ZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09" key_id: ["kdlGe9eVMYugfEhn/0OB5w=="] 0: "kdlGe9eVMYugfEhn/0OB5w==" security_level: 3 session_state: {,…} keybox_system_id: 13701 license_counter: 0 license_id: {request_id: "I8ih0fnhHAH8oYaOavtHsg==", session_id: "I8ih0fnhHAH8oYaOavtHsg==", purchase_id: "",…} purchase_id: "" request_id: "I8ih0fnhHAH8oYaOavtHsg==" session_id: "I8ih0fnhHAH8oYaOavtHsg==" type: "STREAMING" version: 0 signing_key: "/CmTvEpMBviItprLzCMUI+ZexBKX9ePbqIegeJuQO+W7OmKp6LaNOmHCYKQcfvl4TN15FK4N400aPA0gqGyjYg==" status: "OK" supported_tracks: [{type: "AUDIO", key_id: "kdlGe9eVMYugfEhn/0OB5w=="}, {type: "SD", key_id: "k0vHxqApPVueJhpzJHd1Aw=="},…] 0: {type: "AUDIO", key_id: "kdlGe9eVMYugfEhn/0OB5w=="} 1: {type: "SD", key_id: "k0vHxqApPVueJhpzJHd1Aw=="} key_id: "k0vHxqApPVueJhpzJHd1Aw==" type: "SD" 2: {type: "HD", key_id: "OaEsK0CYMP6oL2al1ve9pw=="} key_id: "OaEsK0CYMP6oL2al1ve9pw==" type: "HD" system_id: 13701
To get these kind of response, the request contain a long payload encrypted :
Ä"e
c MÙF{×1 |HgÿCçcastlabs"$eyJhc3NldElkIjoiZGFzaGNlbmNfTTYifQ==2default#È¡Ñùáü¡jûG² è0BÐ! license.widevine.com¹Ì3:/wãð¤g¡ýݲ`x?·Ñ;X6òþÄSË_¨áJHD#¼l5LîËë~¿ùOãÔböî(j²¤ÃAÀqÓ/[¨FÑKïÍÕ!Ï\&7fçiòý",×hªðMÂÿTöܶè³,WüÇøboL<A¥Àª¸ß-ÊÇ¡;Æ´ë²× û£Èy U·)>æÝ~@3Swð8õ
¾gD§|èðO@©{½F9ª²hLfÞºLú?yÌ@ v ïyâ?)ÚÃcÞëÎ>ÝõÝQö×ájEè%Û^æòÎý²µ|pÌâ¢_=@*çg}Ð=SÀy/áRVÖK7»P2[å涤%Öïd¨X¼pÂÜû\~>ø:$ FljÚíþxÜx«½ó^å}Å\=ÔÒìºæ3ßçIàR
Ĥp¢Ñ©ì~ng>Ox °#) 9Y456«(?òS øÑêx¦vTLùÂýçHÛLÑ®|ë,¾^âëÕåqÎcæae¢°&@úõ!"ã¯WÐÙXHr6Ì Xÿ!gqÛ¡±"¢¡~ßËL'ͨÑ~EXΩÕÔ^f|Ãò&t\ÉÁtºØ1Á¡÷Ý8¯ãÞ4X´ÍnÍ6!e.\ Æ6ù>6°6f=ÿ¢o,UUæW×ç¯r¡BQàdrÙÙ>=k)üMO¥Î9@etê|k'3N« Ãò@´k8ÚöÉýs=ìMªvíH <òÓ2Ôpê£f4tr@0J^¥!5C\rÚ§m½Âç:ÞïééS/ê¹.E´SdF8¸.ðÐnÐËý½Ð °Â_JÍäz¬¡B¢GGo=ªRüÞ÷Þ¦ýWKÕÀÀzï#¼ã¸ªÐCN É»u฻_°Bm¡ qãëãÅr&L ç× 0ú&3µoÆïõ,CÛ @¹¿+ Ê å<Ef CÔûuÐ VZzûrå)ß "Ý®Ò^;ÆJÏW²í?ÚkhÉ-Uí&à Ö4dÅc-á_ÍßÉK.YZ8Æò×Ϊð[ºC,U_ NpWÈr¶'8+wĵúuºa"È¿pÍ\Y=f×]L ¬üÙ
>Ï÷2Ûj¼ìàÁ ô¶Ûɹ·ü[µÎ¡ÓjSE§ZÜPóëmÕÞeyý2ßb^u\DlÒëÅVu¬8ÌÅP|Uò4åRñõ&F£±<]X;+~N¹)ñÔHr5Ò.®%Ðh2%ªº ã%H§êö^sKFÌÑÛüén|RTì»bà¨ÀX8)¢½'aFKQø%&
:§±²-àø8]>d³ÛU¤e£ÍbÞådÏàÝ#±mJû%Þèø6¬O¥N?ð+&I¨\ ªå)DfðÁ#¬Uÿk 6zbù»ùù%8Ì$Èmd+Mo6¶V¿¼'Âm YZ,Ò#¤²Õðpªæ®åp±dïÐjÿǹ¬°pÛV;ãÎ_ѱiÆøAhõPxìß·Òð( àù ©ÀXÐ)%׫¾µ&T¥o|8ã µÔ 9cðp}ØúÌn½þ/"°õf~AÊì¯Ô¬6v=vX-Ç2
Òî:®òQ <6÷lÆSóå^r\W(\"ZX¶ÐµãjÔItÚÜH-0dÂjOµÂsÏÓf¢°Y ÐÌ0ûù8éðæCÔÎØMP=bû#¯frèË3ðp"b@êØq¹:«EçëYÚ}\Þ¢ÓKF[.±0¬E½±n7¶v3ß^'åÕTÖû½Ô<z×]AT©¼Øµ;Íg͹'ÕD9üµoU#ùNåv ®µbK¸$Fdü ¸%¹®ÈßÿÁJ ìHV0!ë'da#¤ê¦¶"g¹ï7¤Ck>HÍ}BÕn'oÞßöiHçaÃÒÔC{_4Ýþ¸£¦È\Ö×åaè÷ØjÝ3 7yzä«5MPÙÔÛb[càX6nõñÕAÕ¢ ¾Ó-<v$üêx¼Büôë×jbæüÜ÷ìã $å76þ8Ð#Éôk%ïÙ©Ø]駬¨öE à<nZüèùßoñ?=úIx áÐ^x^ òL6Úwy%ËéY¿K }æf7ðÓbÀp/ä]ç¼5Ï_(þºÜ\·DI¯×XCRì/¬R×®_EÛ#c×ZÈÏ gx<Èà)Öýsâ8÷ßjà°Íh÷\WYazÞî ÑSçUxçá¬J½Åì{#$äH¼µè©×ãIÂÓ¹*CÊÈp?Ü¥48D³^húSì_òeù¦<©¥ÆCpJÊ9¼í4uUÆ@µóì;ÉƦúÛ~2z¸¬£UJMTK!±4M!/òô0DnHVpë´V¦xj¥ ¹¸71q%ß$öúç9ËÕõÖÞwcж 7\½-/>¦¸F4/X ìíÍJ¨G-
½¹DSUÑ;_;g¡4ÐeAÍ©sîÑ'M1â¤ÙrMº1óBµÇx«.{þ¦Õþeí/Ï벳ü
ë7Jå|Ís¿ê,ÛSÕcæM<×/Lò0ã3Ò.Ùdã9îã#@ñ×ÄYo%F SÛé ëB@¦®µëkOhMv1Î+?3$ëÓb·-ùFóKE
){ìó0´qÖNÅ ÛÆÕão×ÊøÑ )ôw2NTlöSÐD< 2É"¶U®©myÍÓNuàKÁ[B4hò±VÑ¡ÜÄ·ó³áÞÃ~ô¸À£yìjå3"ëð{ð²#Ç(yc&ñ.V¤2çë áp/±ØÜJë×}mU«³ºØ ®F@¢Äùq(ÚCu.ÆôHpÞ®$;p/Äqåòôen¹e·4ôX½/¦Ut"ºªÈí%tñ3ôe úF_«%ÁÖ Æ»n(FG5¹¹¦s!å³{RΪÖú.òÚòdUÔ¡O"u©}ÿ¶5§6û,üP7¤í°Åóù==Ö :L=ÈVS¤ >]Úh7×Kþï ¥0[ìÝc)¯D2ק¹aÊ#¨hÊYOfuj.·øtäiærLGûË÷ËTJ}ý°üéï÷Ki¦ E¿óP<Ê©·D ¬ÍBSO}bVÿkN÷¶rD:®½V§uÁÅõ +ÌÚËëD93°É3̤)}{ácöHs?}|Ê»Â2Ø1PHÁt°¥VèZCçûåx%TüKNYTSÎöZØwð":áTY)¾.ë¥Î"J¤ê,ä×kúó¿yÚR£RìbO!®oèåOµÛKÆ])OPWðà'&n p¨é!¬öÌñ¢| â&f¸ÆîQ/Ø6 æ?ï¨5,Di$³Ûscñäæ[3oH×vûbþ%:vÂtÁ´ùÒØ+×vµñǨ¤ØZwɲUnêö]Æ2áj°T¦ióG¡ßCE4V+òìêæ yQ0©ÅÇfãm{bÂ>{ÝQÒ/\ýb.ÀyþwçàÏ6t3l«,!Íòv¤£/p²Úß¼GÜáü t0ÔÜU¨Äy϶p5üùi9ÈÞg(ó¾-Ãþñô{g£}&@ü 42[ñÃ3¨
Éà§eS¥/~K˳ì8å\9¦ÀÂý5äé¡$ õú>u0ø¸\Q§'¨M¸¶@ª«Õ_ö!K²Ù©Ã ¾Hjü²WçðÊ^ëP¤<*B¢{Úw²êÒÝBnÕS@±ÓðôQ-üógaÜÞ^0Ц«ÿÌ5½þU¾E° ®Ði¸ÑÉxVgáÜ<^+\"®þxÅsI}ýØMÎyä÷¯õÿVz",;{¯ 'ux:Wã{+ ÛÀèDz_NÜG:¨¶eú28òîM÷eÊÐþtpâ'ãlbèuzïÑ0bk ÏÌç"6T¼R«ÌN!«·½ÅO¤øcÑâèf ѯ'XuÅ0&âìËÿÄýäV«â®ô:Åv'*åmb!¼°Z®±8Ò![³O«ÍùW´I4xùÈêþíç>?ÌF÷Àà¨<-Vòçõ¡BÙ0[ÕG ü)iwá¦gAé ¸²2,$+´ãU¨}×åèã/mö´mñ }Ïί´ÎJfÍJ^¥ òûöXÏéÉFhÊ;¢¨ôÖoÔàW_åWÀ§ëiúüÙ'Ø%,â÷.*êùU?ÑD¥¢n$Qõ¯/#§)µ!mì£ZósÛ)ÑQâìA]ìó½qvn?Ö®:y)1i*å¶Z}ÀÐÑý|°?.Ñ((0¶"BB+ÇÞG3G1áÍ/ö6áÔÙqe!ÛìÆg´\TCëc©c§·X4²w£ èf1¥3ÐC}i«Òe9¬Ü¼Wá"!¥Û,m »3,»8,×Ûëï"-òö]Ý|«æÅòÍDw®×d#¥¸ÔGÂè¬8t®Î¸çÕ.}Ò~ë½,ö[§K¢ß׺
¢4 ûìHOEü$QϺ¶w0úÈ÷}ÎÒØ$ÀíÌ°¹Ô: ·¥JÇÞÁ #Ò®Dp´"Â^° ä"·ßð'ÿ{
« À´ÁOñ#z¥7&b 4_Á¸D$¦¢¸ Ø?ûW¸>ÊzF¯«o³}çOOkH]i÷5ïW¤·Åª&|¾Ã>æôâ?\¯}rÑ#;c<´ä¦ÚdÙBt71¬)ZL(ÒSª±Û³!tgYyÈ LÁäѼGZÛ=v"nÝ9ÈVzÞ^ÊÜL! ( Ãý>E¬ñÁÈ-ûÛÙ\¤MF<3îÙ|ÉÎõ~m»Ô%Æõe¶¿ÿÙÛ§ nĹý¶~%X)àÇ»rÌ·§::¬KÜÓÖdîV6IÃÙÍÒÆx>XH~Je<óW·eÈW&Ê)n¹Ï<¸i¨lÑj«ø=°"[ÚníáIT¯ù½ ª×?z¬´øDQ.¤1ïPÓCsw¡çÛÍ. àí÷·ÅpIz=ƯS<Eú%QÔc.©ÎÆp=¥LOie;ÆïÊfl×B[DÃÒæ¨+VÈZ!¸á&Æꨣ¿¨Wz)=ªo½» @smyÝ>ÐSPº«8)Û£)þ7×Z¾ò¾DèÚàn$IÙY¹²Ø$x
So considering all that, I have two main questions: 1) Does my player requires, the licence value contained on the "server_certificate" json response , to be able to play content (currently not working) or should i pass it the licence from "LICENCE" json response ? 2) In the case where i would have to pass the licence from "LICENCE" json response to the player to play the stream, How is the payload request formed , what kind of information it contains and how can i get it ? is it base64 encoded, i tried to decrypt it, but nothing really clear with a regular base64 decoder...
Sorry for the long post, I never did that and i'm really lost on it, Anne
@annetube if you use exoplayer, you can overwrite the part where the license server does it's request with your license server format logic. There is no need that you reinvent the wheel, the complete logic is already part of exoplayer (IIRC something with PostRequestHandler, look around here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L392
Edit: I would also suggest that you set some breakpoints in the source file I linked above and play one of the widevine samples which come with the exoplayer app. The workflow is not very complex.
@annetube if you use exoplayer, you can overwrite the part where the license server does it's request with your license server format logic. There is no need that you reinvent the wheel, the complete logic is already part of exoplayer (IIRC something with PostRequestHandler, look around here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L392
Edit: I would also suggest that you set some breakpoints in the source file I linked above and play one of the widevine samples which come with the exoplayer app. The workflow is not very complex.
@peak3d Thanks for answering ,I know its not really about your plugin so i really appreciate your help. On my previous post I already did that youre suggesting , i'm able to get the server_certificate using exoplayer demo just added the post body data="\b\u0004".getBytes(); on the method executepost on httpmediaDRMcallback and added this item on the mediaexolist.json of exoplayer demo (on assets):
{ "name": "M6", "uri": "https://m6.live.6cloud.fr/out/u/m6_sd_p.mpd", "drm_scheme": "widevine", "drm_license_url": "https://lic.staging.drmtoday.com/license-proxy-widevine/cenc/", "drm_key_request_properties": { "x-dt-auth-token": "tcImFjY291bnRpbmdJZFwiOlwiXCIsXCJhc3NldElkXCI6XCJNNlwiLFwicHJvZmlsZVwiOntcInB1cmNoYXNlXCI6e319LFwib3V0cHV0UHJvdGVjdGlvblwiOntcImRpZ2l0YWxcIjpmYWxzZSxcImFuYWxvZ3VlXCI6ZmFsc2UsXCJlbmZvcmNlXCI6ZmFsc2V9fV0iLCJpYXQiOjE1NjA0MTUyMjYsImp0aSI6IkJDY2I0MEJcL0tqVkh4T1lLaWhnM0hRPT0ifQ.H9L_hjdMoZ7E5VKSLJWkI_K1C0N8-n96ulKuivwG6ekuZ_kQUuPvtudTIHrM0yMAs7cahKAAocyVt1yKxLr30Q", "Origin": "https://www.6play.fr", "Referer": "https://www.6play.fr/m6/direct", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3041.0 Safari/537.36" } },
this works i able to get the json Server_ceritificate on exoplayer reponse but it cant apparently handle the key provided , i got an error ===>
2019-06-13 18:48:59.080 9668-10273/com.google.android.exoplayer2.demo E/MediaDrm-JNI: Illegal state exception: Failed to handle key response: General DRM error (-2000) 2019-06-13 18:48:59.084 9668-9668/com.google.android.exoplayer2.demo E/EventLogger: internalError [4.94, 284.95, window=0, period=0, drmSessionManagerError] android.media.MediaDrm$MediaDrmStateException: Failed to handle key response: General DRM error at android.media.MediaDrm.provideKeyResponse(Native Method) at com.google.android.exoplayer2.drm.FrameworkMediaDrm.provideKeyResponse(FrameworkMediaDrm.java:172) at com.google.android.exoplayer2.drm.DefaultDrmSession.onKeyResponse(DefaultDrmSession.java:416) at com.google.android.exoplayer2.drm.DefaultDrmSession.access$100(DefaultDrmSession.java:49) at com.google.android.exoplayer2.drm.DefaultDrmSession$PostResponseHandler.handleMessage(DefaultDrmSession.java:479) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.os.HandlerThread.run(HandlerThread.java:65) 2019-06-13 18:48:59.085 9668-10273/com.google.android.exoplayer2.demo E/ExoPlayerImplInternal: Playback error. com.google.android.exoplayer2.ExoPlaybackException: com.google.android.exoplayer2.drm.DrmSession$DrmSessionException: android.media.MediaDrm$MediaDrmStateException: Failed to handle key response: General DRM error
Thats why i was asking : 1) Does my player requires, the licence value contained on the "server_certificate" json response , to be able to play content (currently not working) or should i pass it the licence from "LICENCE" json response ? 2) In the case where i would have to pass the licence from "LICENCE" json response to the player to play the stream, How is the payload request formed , what kind of information it contains and how can i get it ? is it base64 encoded, i tried to decrypt it, but nothing really clear with a regular base64 decoder...
Thanks for your time
1.) Service certificate could be required. You have to search in exoplayer how "private-mode" is enabled. This will switch on automatic server certificate request. Please refer to MediaDRM android documentation, there you will also find lot of useful information about the workflow.
2.) MediaDRM expects all the data you feed as binary data. If your responses are JSON or b64decoded or whatever, you'll have to parse / decode them by your own. As said in previous post, look what data formats are provided from the sample streams (IIRC its pure binary payload),
@thanks for all the informations provided on this topic, it really helped me, such bad i felt so close, i'm giving up unfortunately. All of this its propably too much complicated and abstract for me, after some quick search, i found nothing about "private-mode", however i'm gonna follow your suggestion and look other samples and propably how they do in mobile app when i would have more tie. I spend already weeks on it wthout sucees. Considering all that , congratualations for your work, its really amazing to handle that !
Thanks for the link, i tied as explained but it doesnt change anything, i also tried to decompile the original app, but its still crypted apparently about what i understand the key is extract from json and decode b64.
public byte[] executeKeyRequest(java.util.UUID r4, com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest r5) { / JADX: method processing error / / Error: java.lang.NullPointerException / / r3 = this; r0 = r3.getUpfrontTokenUseCase; if (r0 == 0) goto L_0x0017; L_0x0004: r0 = r0.execute(); Catch:{ Exception -> 0x0017 } r0 = (io.reactivex.Single) r0; Catch:{ Exception -> 0x0017 } r0 = r0.blockingGet(); Catch:{ Exception -> 0x0017 } r0 = (java.lang.String) r0; Catch:{ Exception -> 0x0017 } r1 = r3.delegate; Catch:{ Exception -> 0x0017 } r2 = "x-dt-auth-token"; Catch:{ Exception -> 0x0017 } r1.setKeyRequestProperty(r2, r0); Catch:{ Exception -> 0x0017 } L_0x0017: r0 = r3.delegate; r1 = "Content-Type"; r2 = "text/xml"; r0.setKeyRequestProperty(r1, r2); r0 = r3.delegate; r4 = r0.executeKeyRequest(r4, r5); r5 = "bytes"; Catch:{ JSONException -> 0x0048 } kotlin.jvm.internal.Intrinsics.checkExpressionValueIsNotNull(r4, r5); Catch:{ JSONException -> 0x0048 } r5 = new java.lang.String; Catch:{ JSONException -> 0x0048 } r0 = kotlin.text.Charsets.UTF_8; Catch:{ JSONException -> 0x0048 } r5.
(r4, r0); Catch:{ JSONException -> 0x0048 } r4 = new org.json.JSONObject; Catch:{ JSONException -> 0x0048 } r4. / throw new UnsupportedOperationException("Method not decompiled: fr.m6.m6replay.media.drm.WidevineDrmTodayMediaDrmCallback.executeKeyRequest(java.util.UUID, com.google.android.exoplayer2.drm.ExoMediaDrm$KeyRequest):byte[]"); }(r5); Catch:{ JSONException -> 0x0048 } r5 = "license"; Catch:{ JSONException -> 0x0048 } r4 = r4.getString(r5); Catch:{ JSONException -> 0x0048 } r5 = 0; Catch:{ JSONException -> 0x0048 } r4 = android.util.Base64.decode(r4, r5); Catch:{ JSONException -> 0x0048 } r5 = "Base64.decode(jsonObject…icense\"), Base64.DEFAULT)"; Catch:{ JSONException -> 0x0048 } kotlin.jvm.internal.Intrinsics.checkExpressionValueIsNotNull(r4, r5); Catch:{ JSONException -> 0x0048 } return r4; L_0x0048: r4 = move-exception; r5 = new java.lang.RuntimeException; r4 = (java.lang.Throwable) r4; r0 = "Error while parsing response"; r5. (r0, r4); r5 = (java.lang.Throwable) r5; throw r5;
I trieid to do kind of the same
public byte[] provideKeyResponse(byte[] scope, byte[] response) throws NotProvisionedException, DeniedByServerException { lic = new JSONObject(new String(response)); extract= lic.getString("license"); return mediaDrm.provideKeyResponse(scope,Base64.decode((extract), Base64.DEFAULT)); }
--> getting same error , I also tried using private mode ---> getting a new error like key not found
2019-06-14 11:05:39.766 11754-12073/com.google.android.exoplayer2.demo E/ExoPlayerImplInternal: Playback error. com.google.android.exoplayer2.ExoPlaybackException: android.media.MediaCodec$CryptoException: Crypto key not available at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:1110) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:653) at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:575) at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:326) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:193) at android.os.HandlerThread.run(HandlerThread.java:65) Caused by: android.media.MediaCodec$CryptoException: Crypto key not available
Thanks for your help, when I will get a decent environement i would try with your plugin in Kodi, to see wath request are sent and how reponse are handled.
seems that you are close to the goal, fix your type errors and you could be finished. I don't develop in exoplayer, It's long time ago I looked at the sources -> it's your project.
@peak3d Yes its my project, i know but i just cant found solution, many users wantsthis channels and cmplain about it ... i tried again many times and still go some errors. Could you tell me if the algrytm of your plugin : make just the server request and switch to private mode or it was just a ssuggestion for my case ?
Also i would like try your plugin on debug mode, where should i start to get a kodi environement (with eclipse IDE would be perfect), the only tutorial i found are really out of date and still talk about xbmc. One important thing is i dont have TV... just a laptop.
Thanks in advance for your help...
The 412 response code is not because of the ServerCertificate call. From reading about 412 its more a missing header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412
Investigate which headers are send in browser, and add them all (!)
Edit: If 412 HTTP error is not the one you're searching, pls. provide a new XAuth token to me so I can try to reproduce. You can use my mail (see my githum accound for it)
Thanks @peak3d for your answer, it's been long time i solved the problem of 412 error, by following your advices ( adding 2 bytes were "BS EOT" on payload) so to resume, I'm getting the server certicate key, but exo player can't handle it even after decrypting via base64 decode and setting the parameters to switch the player in private mode.
I know you don't develop anymore on exoplayer, i just would want to know more are the add-on inputstream adaptative works to try to obtain same result, so for example I would like to know if you use something like switching the player on private mode or if it was just a suggestion to avoid the last request to get the license key (not the request for server certificate) , and if you have any suggestions how to use your add-on on development mode without tv?
Btw: It's Anne on wrong account...
I did a short test with your stream data, but added:
this.mediaDrm.setPropertyString("privacyMode","enable");
here:
https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L166
The firrst request is a 2 byte one, but I get the 412 error, so would be great if you provide the solution how to solve this.
Hi its Anne from github,
Thanks very much for your help so far i use a php script to gt the token :
The new one i can provide you
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJvcHREYXRhIjoie1widXNlcklkXCI6XCJhODJjOWMzZmYxNmQ0NjFiYTMxODFhYjI4MWM4MGJlOVwiLFwibWVyY2hhbnRcIjpcIm02XCIsXCJzZXNzaW9uSWRcIjpcIjZwbGF5XCJ9IiwiY3J0IjoiW3tcImFjY291bnRpbmdJZFwiOlwiXCIsXCJhc3NldElkXCI6XCJNNlwiLFwicHJvZmlsZVwiOntcInB1cmNoYXNlXCI6e319LFwib3V0cHV0UHJvdGVjdGlvblwiOntcImRpZ2l0YWxcIjpmYWxzZSxcImFuYWxvZ3VlXCI6ZmFsc2UsXCJlbmZvcmNlXCI6ZmFsc2V9fV0iLCJpYXQiOjE1NjIwNzc0NTksImp0aSI6InRLekFqa1FmMzk3djNjOU51TkpsWkE9PSJ9.2XQTwhIa8i02HmxIL_SDADhcL5VndcsE0tiC_54gKHruv67hJxSBmmEn-x0O1gzROEvYKGDKLrweMKf4mMJO5Q I put also in attachments the php script who i use to make test requets and get new auth token
Thanks again for your time
Le mer. 3 juil. 2019 à 00:04, Markus Pfau notifications@github.com a écrit :
I did a short test with your stream data, but added:
this.mediaDrm.setPropertyString("privacyMode","enable"); here:
The firrst request is a 2 byte one, but I get the 412 error, so would be great if you provide the solution how to solve this.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/peak3d/inputstream.adaptive/issues/279?email_source=notifications&email_token=AL7IBNEXKW4NHFAWZQAFTSTP5NN6RA5CNFSM4HWZOZ3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZBMFKY#issuecomment-507691691, or mute the thread https://github.com/notifications/unsubscribe-auth/AL7IBNCFER24QUN6U5ALU2DP5NN6RANCNFSM4HWZOZ3A .
Maybe you already did, but just in case you need to set the 2bytes payload of the method executePost
i dit it directly on method line 157 with replacing data by
"\b\u0004".getBytes(),
@peak3d
I have query related to similar "mpd" stream license request from Kodi inputstream.adaptive addon I am using addon like below from Kodi (not exoplayer)
item.property['inputstreamaddon'] = 'inputstream.adaptive' item.property['inputstream.adaptive.manifest_type'] = 'mpd' item.property[inputstream.adaptive.license_type'] = 'com.widevine.alpha' item.property['inputstream.adaptive.license_key'] = 'http://[server]/enc|[headers]|R{SSM}|'
But, inputstream addon is NOT posting first 2 bytes viz. '\b\4' as required, instead it's posting 1748 bytes directly and response from key server is HTTP 400 (Bad request). Also, tried by setting property inputstream.adaptive.license_flags='persistent_storage' but still received same HTTP 400.
However, if first two bytes are explicitly added as below, license server gives 'appliction/binary' (its not json base64) response (HTTP 200) with some kind of binary data most probably a server certificate. But inputstream addon doesn't continue to make another 2 requests to reach to actual key for decrypting video stream
item.property['inputstreamaddon'] = 'inputstream.adaptive' item.property['inputstream.adaptive.manifest_type'] = 'mpd' item.property[inputstream.adaptive.license_type'] = 'com.widevine.alpha' item.property['inputstream.adaptive.license_key'] = 'http://[server]/enc|[headers]|\b\4|'
As mentioned in earlier comments inputstream addon needs to send another 2 POST requests to get to the actual key for the video stream, but those POST requests are not made and it fails with 'no keys' error right after first POST request itself with 1748 bytes (validated this with CURL logs from kodi.log).
Please help me understand if I am missing here? How can I play this stream in Kodi?
Thank you for all the help and contribution
I'm still having same problem today when tried Google’s shaka player demo and the widevine integration demo
First 2 bytes are never sent!
@peak3d can you please help? I cloned this repository just yesterday!
The debug .challenge file contains the challenge payload but the data sent to the server is a string thus resulting in invalid licence request or empty request
Please see wiki for instructions
Hi @peak3d ,
I've a question about how inputstream is working get widwine licence by CURL. I'm triying to reproduce programticaly a session to get the licence token, I already know, your plugin imputsream.adaptive can handle it because it's used by catchuptv for this situation.
They provide these informations to inputstream :
When the licence key is requested on Chrome the request is :
When i'm triying to reproduce the request with CURL, i got
Could you please help me to know whats missing on my request to get this licence, it seems be the payload but im not sure of anything,
Kindly Anne