2012-10-07 20 views
6

के साथ काम नहीं करती है। मैं बैकएंड पर टेपेस्ट्री 5 (5.3.5) के साथ एचटीएमएल 5 वीडियो टैग के माध्यम से अपने आईपैड में एक वीडियो स्ट्रीम करना चाहता हूं। आम तौर पर सर्वरसाइड ढांचे को इसमें कोई भूमिका नहीं निभानी चाहिए लेकिन किसी भी तरह से यह करता है।आईपैड पर वीडियो स्ट्रीमिंग टेपेस्ट्री 5

वैसे भी, उम्मीद है कि यहां कोई मेरी मदद कर सकता है। कृपया ध्यान रखें कि मेरी परियोजना बहुत प्रोटोटाइप है और जो मैंने वर्णन किया है वह प्रासंगिक भागों में सरलीकृत/कम हो गया है। अगर लोग अनिवार्य "आप गलत काम करना चाहते हैं" या सुरक्षा/प्रदर्शन नाइटपिक्स के साथ प्रतिक्रिया नहीं देते हैं, तो समस्या के लिए प्रासंगिक नहीं हैं, तो मैं इसकी बहुत सराहना करता हूं।

तो यहाँ यह जाता है:

सेटअप

मैं एक वीडियो एप्पल एचटीएमएल 5 से लिया प्रदर्शन इसलिए मुझे पता है कि प्रारूप एक मुद्दा नहीं है। मेरे पास एक साधारण टीएमएल पेज "प्ले" है जिसमें सिर्फ "वीडियो" टैग है।

समस्या

मैं एक RequestFilter कि संदर्भित वीडियो फ़ाइल को खोलने और ग्राहक को स्ट्रीमिंग द्वारा वीडियो नियंत्रण से अनुरोध हैंडल को लागू करने से शुरू कर दिया। यह मूलभूत है "यदि पथ 'फ़ाइल' से शुरू होता है तो आउटपुटस्ट्रीम प्रतिक्रिया के लिए फ़ाइल इनपुटस्ट्रीम कॉपी करें"। यह क्रोम के साथ बहुत अच्छी तरह से काम करता है लेकिन आईपैड के साथ नहीं। ठीक है, हालांकि, मुझे कुछ शीर्षलेख होना चाहिए जो मुझे याद आ रही हैं इसलिए मैंने ऐप्पल शोकेस को दोबारा देखा और उसी हेडर और सामग्री के प्रकार को शामिल किया लेकिन कोई खुशी नहीं।

अगला, हालांकि, मैं देखता हूं कि क्या होता है यदि मैं फ़ाइल को टी 5 सेवा देता हूं। मैंने वीडियो को वेबपैड संदर्भ में कॉपी किया, मेरे अनुरोध फ़िल्टर को अक्षम कर दिया और वीडियो के स्रोत विशेषता में सरल फ़ाइल नाम डाला। यह क्रोम और आईपैड में काम करता है। मुझे आश्चर्य हुआ और मुझे यह देखने के लिए प्रेरित किया कि टी 5 स्थिर फाइल/संदर्भ अनुरोध कैसे संभालता है। इस प्रकार तक अब तक मुझे यह महसूस हुआ है कि दो अलग-अलग पथ हैं जिन्हें मैंने एक @Path ("संदर्भ:") के साथ एक संपत्ति में हार्डवार्ड "वीडियो स्रोत" को स्विच करके पुष्टि की है। यह फिर से क्रोम पर काम करता है लेकिन आईपैड पर नहीं।

तो मैं वास्तव में यहां खो गया हूं। "सरल संदर्भ" अनुरोधों में यह गुप्त रस क्या है जो इसे आईपैड पर काम करने की अनुमति देता है? कुछ खास नहीं चल रहा है और फिर भी यह काम करता है। समस्या यह है, मैं वास्तव में मेरे webapp संदर्भ से उन vids की सेवा नहीं कर सकते हैं ...

समाधान

तो, यह पता चला है कि इसे http हैडर "रेंज" कहा जाता है कि वहाँ और कहा कि iPad, के विपरीत क्रोम का उपयोग करता है यह वीडियो के साथ। तब "गुप्त सॉस" यह है कि स्थिर संसाधन अनुरोध के लिए सर्वलेट हैंडलर जानता है कि T5 के दौरान रेंज अनुरोधों से निपटने का तरीका कैसे है। यहां मेरा कस्टम कार्यान्वयन है:

 OutputStream os = response.getOutputStream("video/mp4"); 
     InputStream is = new BufferedInputStream(new FileInputStream(f)); 
     try { 
      String range = request.getHeader("Range"); 
      if(range != null && !range.equals("bytes=0-")) { 
       logger.info("Range response _______________________"); 
       String[] ranges = range.split("=")[1].split("-"); 
       int from = Integer.parseInt(ranges[0]); 
       int to = Integer.parseInt(ranges[1]); 
       int len = to - from + 1 ; 

       response.setStatus(206); 
       response.setHeader("Accept-Ranges", "bytes"); 
       String responseRange = String.format("bytes %d-%d/%d", from, to, f.length()); 
       logger.info("Content-Range:" + responseRange); 
       response.setHeader("Connection", "close"); 
       response.setHeader("Content-Range", responseRange); 
       response.setDateHeader("Last-Modified", new Date().getTime()); 
       response.setContentLength(len); 
       logger.info("length:" + len); 

       byte[] buf = new byte[4096]; 
       is.skip(from); 
       while(len != 0) { 

        int read = is.read(buf, 0, len >= buf.length ? buf.length : len); 
        if(read != -1) { 
         os.write(buf, 0, read); 
         len -= read; 
        } 
       } 


      } else { 
        response.setStatus(200); 
        IOUtils.copy(is, os); 
      } 

     } finally { 
      os.close(); 
      is.close(); 
     } 

उत्तर

7

मैं ऊपर से अपना परिष्कृत समाधान पोस्ट करना चाहता हूं। उम्मीद है कि यह किसी के लिए उपयोगी होगा।

तो मूल रूप से समस्या यह प्रतीत होती है कि मैं "रेंज" http अनुरोध शीर्षलेख को अनदेखा कर रहा था जिसे आईपैड पसंद नहीं आया था।संक्षेप में इस हेडर का अर्थ है कि ग्राहक केवल प्रतिक्रिया के एक निश्चित भाग (इस मामले में एक बाइट रेंज) चाहता है।

इस तरह ::

[INFO] RequestLogger Accept:*/* 
[INFO] RequestLogger Accept-Encoding:identity 
[INFO] RequestLogger Connection:keep-alive 
[INFO] RequestLogger Host:mars:8080 
[INFO] RequestLogger If-Modified-Since:Wed, 10 Oct 2012 22:27:38 GMT 
[INFO] RequestLogger Range:bytes=0-1 
[INFO] RequestLogger User-Agent:AppleCoreMedia/1.0.0.9B176 (iPad; U; CPU OS 5_1 like Mac OS X; en_us) 
[INFO] RequestLogger X-Playback-Session-Id:BC3B397D-D57D-411F-B596-931F5AD9879F 

इसका मतलब है कि आईपैड केवल पहली बाइट चाहता है एक iPad एचटीएमएल वीडियो अनुरोध दिखता है। यदि आप इस हेडर को अनदेखा करते हैं और पूरे शरीर के साथ 200 प्रतिक्रिया भेजते हैं तो वीडियो नहीं खेलेंगे।

[INFO] RequestLogger Content-Range:bytes 0-1/357772702 
[INFO] RequestLogger Content-Length:2 

इसका मतलब यह है "मैं तुम्हें 357,772,702 का 1 कुल उपलब्ध बाइट्स के माध्यम से 0 बाइट भेज रहा": तो, आप एक 206 प्रतिक्रिया (आंशिक प्रतिक्रिया) भेजने के लिए और निम्नलिखित प्रतिक्रिया हेडर सेट की जरूरत है।

जब आप वास्तव में वीडियो चलाना शुरू, अगले अनुरोध इस (रेंज हैडर के अलावा सब कुछ ommited) तरह दिखेगा:

OutputStream os = response.getOutputStream("video/mp4"); 

     try { 
       String range = request.getHeader("Range"); 
       /** if there is no range requested we will just send everything **/ 
       if(range == null) { 
        InputStream is = new BufferedInputStream(new FileInputStream(f)); 
        try { 
         IOUtils.copy(is, os); 
         response.setStatus(200); 
        } finally { 
         is.close(); 
        } 
        return true; 
       } 
       requestLogger.info("Range response _______________________"); 


       String[] ranges = range.split("=")[1].split("-"); 
       int from = Integer.parseInt(ranges[0]); 
       /** 
       * some clients, like chrome will send a range header but won't actually specify the upper bound. 
       * For them we want to send out our large video in chunks. 
       */ 
       int to = HTTP_DEFAULT_CHUNK_SIZE + from; 
       if(to >= f.length()) { 
        to = (int) (f.length() - 1); 
       } 
       if(ranges.length == 2) { 
        to = Integer.parseInt(ranges[1]); 
       } 
       int len = to - from + 1 ; 

       response.setStatus(206); 
       response.setHeader("Accept-Ranges", "bytes"); 
       String responseRange = String.format("bytes %d-%d/%d", from, to, f.length()); 

       response.setHeader("Content-Range", responseRange); 
       response.setDateHeader("Last-Modified", new Date().getTime()); 
       response.setContentLength(len); 

       requestLogger.info("Content-Range:" + responseRange); 
       requestLogger.info("length:" + len); 
       long start = System.currentTimeMillis(); 
       RandomAccessFile raf = new RandomAccessFile(f, "r"); 
       raf.seek(from); 
       byte[] buf = new byte[IO_BUFFER_SIZE]; 
       try { 
        while(len != 0) { 
         int read = raf.read(buf, 0, buf.length > len ? len : buf.length); 
         os.write(buf, 0, read); 
         len -= read; 
        } 
       } finally { 
        raf.close(); 
       } 
       logger.info("r/w took:" + (System.currentTimeMillis() - start)); 




     } finally { 
      os.close(); 

     } 

:

[INFO] RequestLogger Range:bytes=0-357772701 

तो मेरी परिष्कृत समाधान इस तरह दिखता है यह समाधान तब पहला बेहतर है क्योंकि यह "रेंज" अनुरोधों के लिए सभी मामलों को संभालता है जो कि क्रोम जैसे क्लाइंट के लिए वीडियो के भीतर छोड़ने में सक्षम होने के लिए प्रीरैक प्रतीत होता है (जिस बिंदु पर वे इसके लिए एक रेंज अनुरोध जारी करेंगे वीडियो में इंगित करें)।

हालांकि यह अभी भी सही नहीं है। आगे की सुधार "अंतिम-संशोधित" शीर्षलेख को सही ढंग से सेट कर रही है और क्लाइंट की उचित हैंडलिंग करने से अमान्य सीमा या बाइट्स के बाद किसी और चीज की आवश्यकता होती है।

+0

यह उपयोगी जानकारी है; ऐसा कोई कारण नहीं है कि टेपेस्ट्री मानक मानक हैंडलिंग कोड के अंदर स्वचालित रूप से इसे संभाल नहीं सकता है; हम सिर्फ इतना नहीं जानते कि इसे करने की ज़रूरत है। हमारे जेआईआरए को इस स्तर की जानकारी जोड़ना पहला कदम है। –

+0

उत्कृष्ट जवाब। तुरंत एक आकर्षण की तरह काम करता है। बहुत बहुत धन्यवाद। –

0

मुझे संदेह है कि यह टेपेस्ट्री के मुकाबले आईपैड के बारे में अधिक है।

मैं प्रतिक्रिया को स्ट्रीम लिखने से पहले Response.disableCompression() का आह्वान कर सकता हूं; टेपेस्ट्री आपकी स्ट्रीम को जीजेआईपीआईपी करने की कोशिश कर रहा है, और आईपैड इसके लिए तैयार नहीं हो सकता है, क्योंकि वीडियो और छवि प्रारूप आमतौर पर पहले ही संपीड़ित होते हैं।

इसके अलावा, मुझे एक सामग्री प्रकार हेडर सेट नहीं दिख रहा है; फिर से आईपैड क्रोम की तुलना में बस अधिक संवेदनशील हो सकता है।

+0

हाय हावर्ड। मुझे लगता है कि यह बहुत अच्छा है कि आप Stackoverflow पर T5 (एक महान ढांचा) का जवाब देने के लिए समय लेते हैं। वैसे भी, मुझे पता चला कि समस्या क्या थी और मेरे प्रश्न के समाधान को जोड़ा गया। टीएल; डीआर संस्करण यह है कि यदि आप "रेंज" http अनुरोध शीर्षलेख की उपेक्षा करते हैं तो आईपैड इसे पसंद नहीं करता है। यह टी 5 के लिए भी एक समस्या हो सकती है क्योंकि मैं जो कहता हूं, जब ढांचा एक संपत्ति की सेवा कर रहा है तो यह रेंज हेडर को भी नजरअंदाज कर देगा। मैं अधिक जानकारी के साथ एक उत्तर पोस्ट करूंगा। – Wulf