android - Buffered वीडियो के साथ एंड्रॉइड वीडियो व्यू अभिविन्यास परिवर्तन




android-orientation android-videoview (7)

मैं एंड्रॉइड मार्केटप्लेस में नवीनतम यूट्यूब ऐप की कार्यक्षमता को अनिवार्य रूप से दोहराने की कोशिश कर रहा हूं। वीडियो देखते समय दो अलग-अलग लेआउट होते हैं, एक पोर्ट्रेट में जो अतिरिक्त जानकारी प्रदान करता है, और एक परिदृश्य में जो वीडियो का पूर्ण स्क्रीन दृश्य प्रदान करता है।


पोर्ट्रेट मोड में यूट्यूब ऐप


लैंडस्केप मोड में यूट्यूब ऐप

(तस्वीरों की यादृच्छिकता के लिए खेद है, लेकिन वे वास्तविक चित्रों के बारे में पहली तस्वीरें थीं)

यह सामान्य रूप से करना बहुत आसान है - बस लेआउट-भूमि में वैकल्पिक लेआउट निर्दिष्ट करें और सभी अच्छे होंगे। यूट्यूब ऐप वास्तव में अच्छी तरह से काम करता है (और जो मैं दोहराने की कोशिश कर रहा हूं) यह है कि अभिविन्यास परिवर्तन पर, वीडियो खेलना जारी रखता है और शुरुआत से फिर से बफर नहीं करना पड़ता है।

मुझे पता चला है कि कॉन्फ़िगरेशन चेंज () पर ओवरराइडिंग और नए लेआउट पैरामीटर सेट करने से मुझे वीडियो को रीफफर करने के बिना वीडियो का आकार बदलने की अनुमति मिल जाएगी - हालांकि वीडियो कई बार स्क्रीन को घूर्णन करते समय यादृच्छिक रूप से विभिन्न चौड़ाई / ऊंचाई पर स्केल करेगा। मैंने VideoView पर सभी प्रकार की अमान्य () कॉल करने का प्रयास किया है, माता-पिता रिलेटिव लेआउट कंटेनर पर RequestLayout () को कॉल करने का प्रयास किया है और बस मैं जितनी अलग चीज़ें कर सकता हूं, लेकिन मुझे इसे ठीक से काम करने के लिए प्रतीत नहीं होता है। किसी भी सलाह की काफी सराहना की जाएगी!

मेरा कोड यहाँ है:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        questionText.setVisibility(View.GONE);
        respond.setVisibility(View.GONE);
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    } else {
        questionText.setVisibility(View.VISIBLE);
        respond.setVisibility(View.VISIBLE);
        Resources r = getResources();
        int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150.0f, r.getDisplayMetrics());
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, height));
    }
}

संपादित करें: मैंने लॉककैट में कुछ रोचक आउटपुट खोजे हैं जो मेरे वीडियो को घुमाए जाने पर आता है जो अपराधी प्रतीत होता है - हालांकि मुझे नहीं पता कि इसे कैसे ठीक किया जाए:

ठीक से आकार बदलते समय लॉगकैट आउटपुट (पूरी विंडो लेता है)

एच = 726 नोटिस

12-13 15:37:35.468  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=210}
12-13 15:37:35.561  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Adjusted Position/X1/Y0/W403/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Rotation/90
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=402 h=726
12-13 15:37:35.561  1262  1268 I Overlay : dumping driver state:
12-13 15:37:35.561  1262  1268 I Overlay : output pixfmt:
12-13 15:37:35.561  1262  1268 I Overlay : w: 432
12-13 15:37:35.561  1262  1268 I Overlay : h: 240
12-13 15:37:35.561  1262  1268 I Overlay : color: 7
12-13 15:37:35.561  1262  1268 I Overlay : UYVY
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:37:35.561  1262  1268 I Overlay : window l: 1 
12-13 15:37:35.561  1262  1268 I Overlay : window t: 0 
12-13 15:37:35.561  1262  1268 I Overlay : window w: 402 
12-13 15:37:35.561  1262  1268 I Overlay : window h: 726

गलत तरीके से आकार बदलते समय लॉगकैट आउटपुट (पूर्ण स्क्रीन के छोटे हिस्से को लेता है)

एच = 480 ध्यान दें

12-13 15:43:00.085  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=216}
12-13 15:43:00.171  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Adjusted Position/X138/Y0/W266/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Rotation/90
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=266 h=480
12-13 15:43:00.179  1262  1268 I Overlay : dumping driver state:
12-13 15:43:00.179  1262  1268 I Overlay : output pixfmt:
12-13 15:43:00.179  1262  1268 I Overlay : w: 432
12-13 15:43:00.179  1262  1268 I Overlay : h: 240
12-13 15:43:00.179  1262  1268 I Overlay : color: 7
12-13 15:43:00.179  1262  1268 I Overlay : UYVY
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:43:00.179  1262  1268 I Overlay : window l: 138 
12-13 15:43:00.179  1262  1268 I Overlay : window t: 0 
12-13 15:43:00.179  1262  1268 I Overlay : window w: 266 
12-13 15:43:00.179  1262  1268 I Overlay : window h: 480

शायद कोई जानता है कि 'ओवरले' क्या है और इसे सही ऊंचाई मूल्य क्यों नहीं मिल रहा है?


यूट्यूब ऐप को दोहराएं

मैं एक नमूना प्रोजेक्ट बनाने में कामयाब रहा जिसके लिए android:configChanges="orientation" आवश्यकता नहीं है android:configChanges="orientation" या कस्टम VideoView । परिणामस्वरूप अनुभव वीडियो प्लेबैक के दौरान यूट्यूब ऐप रोटेशन को कैसे संभालता है, यह समान है। दूसरे शब्दों में, वीडियो को रोका, पुनः-बफर या पुनः लोड करने की आवश्यकता नहीं है, और जब डिवाइस अभिविन्यास बदलता है तो कोई भी ऑडियो फ्रेम छोड़ या छोड़ नहीं देता है।

इष्टतम विधि

यह विधि MediaPlayer के वर्तमान वीडियो फ्रेम के लिए एक सिंक के रूप में एक TextureView और इसके साथ SurfaceTexture का उपयोग करता है। चूंकि एक भूतलटेक्चर एक जीएल बनावट वस्तु का उपयोग करता है (बस जीएल संदर्भ से एक पूर्णांक द्वारा संदर्भित), मान लें कि कॉन्फ़िगरेशन परिवर्तनों के माध्यम से सतह सतह के संदर्भ को बनाए रखना ठीक है। TextureView स्वयं को कॉन्फ़िगरेशन परिवर्तन (बैकिंग गतिविधि के साथ) के दौरान नष्ट और पुनर्निर्मित किया जाता है, और नए बनाए गए TextureView को संलग्न होने से पहले सतह के संदर्भ संदर्भ के साथ बस अपडेट किया जाता है।

मैंने एक पूर्ण कामकाजी उदाहरण बनाया है जो दिखाता है कि आपके मीडियाप्लेयर को कैसे और कब प्रारंभ करना है और एक संभावित मीडियाकंट्रोलर, और मैं नीचे दिए गए इस प्रश्न के लिए प्रासंगिक दिलचस्प भागों को हाइलाइट करूंगा:

public class VideoFragment {

    TextureView mDisplay;
    SurfaceTexture mTexture;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        mDisplay = (TextureView) rootView.findViewById(R.id.texture_view);
        if (mTexture != null) {
            mDisplay.setSurfaceTexture(mTexture);
        }
        mDisplay.setSurfaceTextureListener(mTextureListener);

        return rootView;
    }

    TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
            // Initialize your media now or flag that the SurfaceTexture is available..
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            mTexture = surface;
            return false; // this says you are still using the SurfaceTexture..
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            mTexture = surface;
        }
    };

    @Override
    public void onDestroyView() {
        mDisplay = null;
        super.onDestroyView();
    }

    // ...

}

चूंकि समाधान मैन्युअल रूप से कॉन्फ़िगरेशन परिवर्तनों को नियंत्रित करने वाली गतिविधि के बजाय एक बनाए गए टुकड़े का उपयोग करता है, इसलिए आप कॉन्फ़िगरेशन विशिष्ट संसाधन मुद्रास्फीति प्रणाली को पूरी तरह से लाभ उठा सकते हैं जैसा कि आप स्वाभाविक रूप से करेंगे। दुर्भाग्यवश, यदि आपका न्यूनतम एसडीके एपीआई 16 से नीचे है, तो आपको अनिवार्य रूप से TextureView (जो मैंने नहीं किया है) का बैकपोर्ट करने की आवश्यकता होगी।

अंत में, यदि आप रुचि रखते हैं, तो मेरे प्रारंभिक प्रश्न की जांच करें : मेरा मूल दृष्टिकोण, एंड्रॉइड मीडिया स्टैक, यह क्यों काम नहीं किया, और वैकल्पिक कामकाज।


VideoView एक ओवरले कहलाता है जो एक ऐसा क्षेत्र है जहां वीडियो प्रदान किया जाता है। वीडियो ओवरव्यू रखने वाली विंडो के पीछे वह ओवरले है। VideoView अपनी खिड़की में एक छेद पेंच करता है ताकि ओवरले दिखाई दे। इसके बाद यह लेआउट के साथ सिंक में रहता है (उदाहरण के लिए। यदि आप VideoView को स्थानांतरित या आकार बदलते हैं, तो ओवरले को स्थानांतरित और आकार बदलना होगा)।

लेआउट चरण के दौरान कहीं भी एक बग है जो ओवरले को वीडियोव्यू द्वारा पिछले आकार सेट का उपयोग करता है।

इसे ठीक करने के लिए, उपclass VideoView और ओवरराइड ऑनआउट:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    getHolder().setSizeFromLayout();
}

और ओवरले में VideoView के लेआउट आयामों का सही आकार होगा।


जबकि मार्क 37 (बहुत उपयोगी) answer काम करता है, इसके लिए आयामों को मैन्युअल रूप से सेट करने की आवश्यकता होती है (setDimensions का उपयोग करके)। यह एक ऐप में ठीक हो सकता है जहां वांछित आयाम अग्रिम में ज्ञात हैं, लेकिन यदि आप चाहते हैं कि विचार वीडियो के पैरामीटर के आधार पर स्वचालित रूप से आकार निर्धारित करें (उदाहरण के लिए मूल पहलू अनुपात रखा गया है), तो एक अलग दृष्टिकोण की आवश्यकता है ।

सौभाग्य से, यह निर्धारित करता है कि सेटडिमेन्शन भाग वास्तव में आवश्यक नहीं है। VideoView के onMeasure पहले से ही सभी जरूरी तर्क शामिल हैं, इसलिए सेट पर कॉल करने के लिए किसी पर भरोसा करने के बजाय, बस सुपरऑन को कॉल करना संभव है, और फिर दृश्य आकार प्राप्त करने के लिए दृश्य की getMeasuredWidth / getMeasuredHeight का उपयोग करें।

तो, VideoViewCustom क्लास बस बन जाता है:

public class VideoViewCustom extends VideoView {

    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        getHolder().setFixedSize(getMeasuredWidth(), getMeasuredHeight());
    }
}

इस संस्करण को कॉलर में किसी भी अतिरिक्त कोड की आवश्यकता नहीं है, और VideoView के मौजूदा ऑनमेजर कार्यान्वयन का पूर्ण लाभ लेता है।

यह वास्तव में ज़ेपेक द्वारा सुझाए गए approach समान है, जो मूल रूप से वही बात करता है, केवल ऑनर के बजाय ऑनआउट के साथ।


न्यूनतम कोड के साथ आप जो चाहते हैं उसे पूरा करने के लिए यहां एक आसान तरीका है:

AndroidManifest.xml:

android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

नोट: अपने एपीआई के लिए आवश्यकतानुसार संपादित करें, इसमें 10+ शामिल हैं, लेकिन कम एपीआई को इस लाइन के "स्क्रीनसाइज | स्क्रीनलेआउट | यूईमोड" हिस्से को हटाने की आवश्यकता है

"ऑनक्रेट" विधि के अंदर, आमतौर पर "super.onCreate" के अंतर्गत, जोड़ें:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

और फिर कहीं, आमतौर पर नीचे, जोड़ें:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

प्लेबैक में बाधा डाले बिना अभिविन्यास बदल गया है और केवल कॉन्फ़िगरेशन विधि को ओवरराइड करने की आवश्यकता होने पर यह वीडियो को पूर्णस्क्रीन में बदल देगा।


मैं VideoView क्लास में मेमरी फ़ंक्शन पर समस्या को कम करने में सक्षम था। एक बाल वर्ग बनाकर और ऑनमेर फ़ंक्शन को ओवरराइड करके, मैं वांछित कार्यक्षमता प्राप्त करने में सक्षम था।

public class VideoViewCustom extends VideoView {

    private int mForceHeight = 0;
    private int mForceWidth = 0;
    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setDimensions(int w, int h) {
        this.mForceHeight = h;
        this.mForceWidth = w;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("@@@@", "onMeasure");

        setMeasuredDimension(mForceWidth, mForceHeight);
    }
}

फिर मेरी गतिविधि के अंदर मैंने निम्नलिखित किया:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        questionVideo.setDimensions(displayHeight, displayWidth);
        questionVideo.getHolder().setFixedSize(displayHeight, displayWidth);

    } else {
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        questionVideo.setDimensions(displayWidth, smallHeight);
        questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

    }
}

रेखा:

questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

यह काम करने के लिए महत्वपूर्ण है। यदि आप इस लड़के के बिना सेट डिमेंशन कॉल करते हैं, तो वीडियो अभी भी आकार बदल नहीं पाएगा।

आपको केवल एक और चीज करने की ज़रूरत है यह सुनिश्चित करने के लिए कि आप onCreate () विधि के अंदर setDimensions () को कॉल करते हैं या आपका वीडियो बफरिंग शुरू नहीं करेगा क्योंकि वीडियो किसी भी आकार की सतह पर ड्रॉ करने के लिए सेट नहीं होगा।

// onCreate()
questionVideo.setDimensions(initialWidth, initialHeight); 

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

उम्मीद है कि यह भविष्य में किसी की मदद करता है!

संपादित करें: (जून 2016)

यह उत्तर बहुत पुराना है (मुझे लगता है कि एंड्रॉइड 2.2 / 2.3) और शायद नीचे दिए गए अन्य उत्तरों के रूप में प्रासंगिक नहीं है! उन्हें तब तक देखें जब तक आप विरासत एंड्रॉइड पर देवता नहीं ले रहे :)


मैंने अन्य उत्तरों के कोड का उपयोग करने की कोशिश की, लेकिन यह मेरी समस्या का समाधान नहीं किया, क्योंकि अगर मैंने स्क्रीन को जल्दी घुमाया, तो मेरा वीडियो आकार बंप हो गया। इसलिए मैं उनके कोड का हिस्सा उपयोग करता हूं, जिसे मैंने थ्रेड में रखा है जो विंडो को बहुत तेज़ी से अपडेट करता है। तो, मेरा कोड यहाँ है:

  new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            while(true)
            {
                try
                {
                    Thread.sleep(1);
                }
                catch(InterruptedException e){}
                handler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                WindowManager.LayoutParams.FLAG_FULLSCREEN);
                    }
                });
            }
        }
    }).start();

इस तरह, वीडियो का आकार हमेशा सही होगा (मेरे मामले में)।


सबसे पहले, अपने व्यापक जवाब के लिए बहुत बहुत धन्यवाद।

मुझे एक ही समस्या थी, वीडियो रोटेशन के बाद वीडियो व्यू के अंदर ज्यादातर समय छोटा, या बड़ा, या विकृत हो जाएगा।

मैंने आपके समाधान की कोशिश की, लेकिन मैंने यादृच्छिक चीजों की भी कोशिश की, और मौके से मैंने देखा कि यदि मेरा वीडियो व्यू अपने माता-पिता में केंद्रित है, तो यह जादुई रूप से स्वयं काम करता है (कोई कस्टम वीडियो व्यू आवश्यक या कुछ भी नहीं)।

अधिक विशिष्ट होने के लिए, इस लेआउट के साथ, मैं ज्यादातर समय समस्या को पुन: उत्पन्न करता हूं:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

इस एक लेआउट के साथ, मुझे कभी समस्या नहीं है (साथ ही, वीडियो केंद्रित है, वैसे भी यह वैसे भी होना चाहिए;):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" />

</RelativeLayout>

यह wrap_content बजाय wrap_content साथ भी काम करता है (वीडियो अभी भी सभी जगह लेता है) जो मुझे अधिक समझ में नहीं आता है।

वैसे भी, मेरे पास इसके लिए कोई स्पष्टीकरण नहीं है - यह मेरे लिए एक वीडियो व्यू बग जैसा दिखता है।







android-videoview