ক্যামেরা প্রিভিউ

মনে রাখবেন: এই পৃষ্ঠা Camera2 প্যাকেজকে রেফার করে। আপনার অ্যাপের জন্য Camera2 থেকে নির্দিষ্ট, লো-লেভেল ফিচারের প্রয়োজন না হলে, আমরা CameraX ব্যবহার করার সাজেশন দিই। CameraX এবং Camera2, দুটিই Android 5.0 (API লেভেল ২১) এবং তার পরবর্তী যেকোনও ভার্সনে কাজ করে।

Android ডিভাইসে ক্যামেরা এবং ক্যামেরা প্রিভিউ সবসময় একই ওরিয়েন্টেশনে থাকে না।

ডিভাইসটি ফোন, ট্যাবলেট বা কম্পিউটার যাই হোক না কেন, ডিভাইসে ক্যামেরা একটি নির্দিষ্ট অবস্থানে থাকে। ডিভাইসের ওরিয়েন্টেশন পরিবর্তন হলে, ক্যামেরার ওরিয়েন্টেশনও পরিবর্তন হয়।

এর ফলে, ক্যামেরা অ্যাপ সাধারণত ডিভাইসের ওরিয়েন্টেশন ও ক্যামেরা প্রিভিউয়ের অ্যাস্পেক্ট রেশিওর মধ্যে একটি নির্দিষ্ট সম্পর্ক আছে বলে ধরে নেয়। ফোন পোর্ট্রেট ওরিয়েন্টেশনে থাকলে, ক্যামেরার প্রিভিউ চওড়ার চেয়ে লম্বা হবে বলে ধরে নেওয়া হয়। ফোন (এবং ক্যামেরা) ল্যান্ডস্কেপ মোডে ঘোরানো হলে, ক্যামেরা প্রিভিউ লম্বার চেয়ে চওড়া হবে বলে আশা করা হচ্ছে।

কিন্তু এই অনুমানগুলি নতুন ফর্ম ফ্যাক্টর দ্বারা চ্যালেঞ্জ করা হয়, যেমন ফোল্ডেবল ডিভাইস, এবং ডিসপ্লে মোড যেমন মাল্টি‑উইন্ডো এবং মাল্টি‑ডিসপ্লে। ফোল্ডেবল ডিভাইস ওরিয়েন্টেশন পরিবর্তন না করেই ডিসপ্লে সাইজ এবং অ্যাস্পেক্ট রেশিও পরিবর্তন করে। মাল্টি‑উইন্ডো মোড ক্যামেরা অ্যাপকে স্ক্রিনের একটি অংশে সীমাবদ্ধ করে, ডিভাইসের ওরিয়েন্টেশন যাই হোক না কেন ক্যামেরা প্রিভিউ স্কেল করে। মাল্টি‑ডিসপ্লে মোড সেকেন্ডারি ডিসপ্লে ব্যবহার করার সুবিধা দেয় যা প্রাইমারি ডিসপ্লের মতো একই ওরিয়েন্টেশনে নাও থাকতে পারে।

ক্যামেরার অভিমুখ

Android-এর সাথে মানানসই হওয়ার সংজ্ঞা অনুযায়ী, ক্যামেরার ইমেজ সেন্সর "এমনভাবে ওরিয়েন্ট করতে হবে যাতে ক্যামেরার দীর্ঘ ডাইমেনশন স্ক্রিনের দীর্ঘ ডাইমেনশনের সাথে অ্যালাইন করে। অর্থাৎ, ডিভাইসটি ল্যান্ডস্কেপ ওরিয়েন্টেশনে ধরা হলে, ক্যামেরা অবশ্যই ল্যান্ডস্কেপ ওরিয়েন্টেশনে ছবি ক্যাপচার করবে। ডিভাইসের স্বাভাবিক ওরিয়েন্টেশন যাই হোক না কেন, এটি প্রযোজ্য হবে; অর্থাৎ, এটি ল্যান্ডস্কেপ-প্রাইমারি ডিভাইসের পাশাপাশি পোর্ট্রেট-প্রাইমারি ডিভাইসের ক্ষেত্রেও প্রযোজ্য হবে।"

ক্যামেরা থেকে স্ক্রিনের বিন্যাস ক্যামেরা অ্যাপে ক্যামেরা ভিউফাইন্ডারের ডিসপ্লে এরিয়াকে সর্বাধিক করে। এছাড়াও, ইমেজ সেন্সর সাধারণত ল্যান্ডস্কেপ অ্যাস্পেক্ট রেশিওতে তাদের ডেটা আউটপুট করে, ৪:৩ সবচেয়ে সাধারণ।

ফোন এবং ক্যামেরা সেন্সর দুটিই পোর্ট্রেট ওরিয়েন্টেশনে আছে।
ছবি ১. ফোন ও ক্যামেরা সেন্সর ওরিয়েন্টেশনের সাধারণ সম্পর্ক।

ক্যামেরা সেন্সরের স্বাভাবিক ওরিয়েন্টেশন হল ল্যান্ডস্কেপ। ছবি ১-এ, ফ্রন্ট-ফেসিং ক্যামেরার (ডিসপ্লে যেদিকে মুখ করে আছে, ক্যামেরাটিও সেদিকেই মুখ করে আছে) সেন্সরটি ফোনের সাপেক্ষে ২৭০ ডিগ্রি ঘোরানো আছে যাতে Android কম্প্যাটিবিলিটি ডেফিনিশন মেনে চলা যায়।

অ্যাপে সেন্সর রোটেশন এক্সপোজ করতে, camera2 API-তে SENSOR_ORIENTATION কনস্ট্যান্ট অন্তর্ভুক্ত থাকে। বেশিরভাগ ফোন ও ট্যাবলেটের ক্ষেত্রে, ডিভাইস ফ্রন্ট-ফেসিং ক্যামেরার জন্য ২৭০ ডিগ্রি এবং ব্যাক-ফেসিং ক্যামেরার জন্য ৯০ ডিগ্রি (ডিভাইসের পিছন থেকে দেখার পয়েন্ট) সেন্সর ওরিয়েন্টেশন রিপোর্ট করে, যা ডিভাইসের লম্বা প্রান্তের সাথে সেন্সরের লম্বা প্রান্তকে অ্যালাইন করে। ল্যাপটপের ক্যামেরা সাধারণত ০ বা ১৮০ ডিগ্রির সেন্সর ওরিয়েন্টেশন রিপোর্ট করে।

ক্যামেরা ইমেজ সেন্সর তার ডেটা (ইমেজ বাফার) সেন্সরের স্বাভাবিক ওরিয়েন্টেশনে (ল্যান্ডস্কেপ) আউটপুট করে, তাই ডিভাইসের স্বাভাবিক ওরিয়েন্টেশনে ক্যামেরা প্রিভিউ সোজাভাবে দেখানোর জন্য ইমেজ বাফারকে SENSOR_ORIENTATION দ্বারা নির্দিষ্ট করা ডিগ্রির সংখ্যায় ঘোরাতে হবে। সামনের ক্যামেরার ক্ষেত্রে, ঘড়ির কাঁটার বিপরীতে এবং পিছনের ক্যামেরার ক্ষেত্রে, ঘড়ির কাঁটার দিকে রোটেট হয়।

যেমন, চিত্র ১-এ ফ্রন্ট-ফেসিং ক্যামেরার জন্য, ক্যামেরা সেন্সর দ্বারা তৈরি করা ইমেজ বাফার এইরকম দেখতে হয়:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে, ছবি
            পাশে, উপরে বাঁদিকে।

ছবিটি ঘড়ির কাঁটার বিপরীতে ২৭০ ডিগ্রি ঘোরাতে হবে যাতে প্রিভিউয়ের ওরিয়েন্টেশন ডিভাইসের ওরিয়েন্টেশনের সাথে মেলে:

পোর্ট্রেট ওরিয়েন্টেশনে ক্যামেরা সেন্সর, ছবি সোজাভাবে দেখানো হয়েছে।

ব্যাক-ফেসিং ক্যামেরা উপরের বাফারের মতো একই ওরিয়েন্টেশন সহ ইমেজ বাফার তৈরি করবে, কিন্তু SENSOR_ORIENTATION হল ৯০ ডিগ্রি। এর ফলে, বাফার ঘড়ির কাঁটার দিকে ৯০ ডিগ্রি ঘোরানো হয়।

ডিভাইসের ঘূর্ণন

ডিভাইস রোটেশন হল, কোনও ডিভাইস তার স্বাভাবিক ওরিয়েন্টেশন থেকে কত ডিগ্রি ঘোরানো হয়েছে। যেমন, ল্যান্ডস্কেপ ওরিয়েন্টেশনে থাকা ফোন ৯০ বা ২৭০ ডিগ্রি ঘোরে, এটি নির্ভর করে কোন দিকে ঘোরানো হচ্ছে তার উপর।

ক্যামেরা প্রিভিউ যাতে সোজা দেখায়, তার জন্য ক্যামেরা সেন্সর ইমেজ বাফারকে ডিভাইস রোটেশনের সমান ডিগ্রিতে (সেন্সর ওরিয়েন্টেশনের ডিগ্রির সাথে) ঘোরাতে হবে।

ওরিয়েন্টেশন ক্যালকুলেশন

ক্যামেরা প্রিভিউয়ের সঠিক ওরিয়েন্টেশন সেন্সর ওরিয়েন্টেশন ও ডিভাইস রোটেশনকে বিবেচনা করে।

সেন্সর ইমেজ বাফারের সামগ্রিক রোটেশন নিম্নলিখিত ফর্মুলা ব্যবহার করে গণনা করা যেতে পারে:

rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360

যেখানে sign হল ফ্রন্ট-ফেসিং ক্যামেরার জন্য 1, ব্যাক-ফেসিং ক্যামেরার জন্য -1।

সামনের ক্যামেরার জন্য, ইমেজ বাফার ঘড়ির কাঁটার বিপরীতে ঘোরানো হয় (সেন্সরের স্বাভাবিক ওরিয়েন্টেশন থেকে)। ব্যাক-ফেসিং ক্যামেরার ক্ষেত্রে, সেন্সর ইমেজ বাফার ঘড়ির কাঁটার দিকে ঘোরানো হয়।

deviceOrientationDegrees * sign + 360 এক্সপ্রেশনটি রিয়ার-ফেসিং ক্যামেরার জন্য ডিভাইসের রোটেশনকে ঘড়ির কাঁটার বিপরীত দিক থেকে ঘড়ির কাঁটার দিকে কনভার্ট করে (যেমন, ঘড়ির কাঁটার বিপরীত দিকে ২৭০ ডিগ্রি থেকে ঘড়ির কাঁটার দিকে ৯০ ডিগ্রিতে কনভার্ট করা)। মডিউলো অপারেশন ফলাফলকে ৩৬০ ডিগ্রির কম স্কেল করে (যেমন, ৫৪০ ডিগ্রি রোটেশনকে ১৮০ ডিগ্রিতে স্কেল করা)।

বিভিন্ন API ডিভাইসের রোটেশন আলাদাভাবে রিপোর্ট করে:

  • Display#getRotation() ডিভাইসটি ঘড়ির কাঁটার বিপরীতে ঘোরানোর (ব্যবহারকারীর দৃষ্টিকোণ থেকে) সুবিধা প্রদান করে। এই ভ্যালু উপরের ফর্মুলায় যেমন আছে তেমনভাবেই প্লাগ-ইন করা হয়।
  • OrientationEventListener#onOrientationChanged() ডিভাইসের ঘড়ির কাঁটার দিকে ঘূর্ণন (ব্যবহারকারীর দৃষ্টিকোণ থেকে) রিটার্ন করে। উপরের ফর্মুলায় ব্যবহার করার জন্য ভ্যালু নেগেট করো।

সামনের দিকের ক্যামেরা

ক্যামেরা প্রিভিউ এবং সেন্সর দুটিই ল্যান্ডস্কেপ ওরিয়েন্টেশনে, সেন্সর
            ডানদিকে উপরে আছে।
ছবি ২. ফোন ৯০ ডিগ্রি ঘুরিয়ে ল্যান্ডস্কেপ ওরিয়েন্টেশনে নিয়ে গিয়ে ক্যামেরা প্রিভিউ ও সেন্সর।

ছবি ২-এ ক্যামেরা সেন্সর দ্বারা তৈরি করা ইমেজ বাফার এখানে দেওয়া হল:

ল্যান্ডস্কেপ ওরিয়েন্টেশনে ক্যামেরা সেন্সর, ছবি সোজাভাবে দেখানো হয়েছে।

সেন্সর ওরিয়েন্টেশন অ্যাডজাস্ট করার জন্য বাফারকে ঘড়ির কাঁটার বিপরীতে ২৭০ ডিগ্রি ঘোরাতে হবে (উপরে ক্যামেরা ওরিয়েন্টেশন দেখুন):

ক্যামেরা সেন্সর পোর্ট্রেট ওরিয়েন্টেশনে ঘোরানো হয়েছে, ছবি পাশে,
            উপরে ডানদিকে।

তারপর, ডিভাইস ঘোরানোর জন্য বাফারটি ঘড়ির কাঁটার বিপরীত দিকে আরও ৯০ ডিগ্রি ঘোরানো হয়, যার ফলে চিত্র ২-এ ক্যামেরা প্রিভিউয়ের সঠিক ওরিয়েন্টেশন পাওয়া যায়:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং ছবি
            সোজা আছে।

এখানে ক্যামেরাটি ডানদিকে ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে:

ক্যামেরা প্রিভিউ এবং সেন্সর দুটিই ল্যান্ডস্কেপ ওরিয়েন্টেশনে আছে, কিন্তু
            সেন্সরটি উল্টো দিকে আছে।
ছবি ৩. ফোন ২৭০ ডিগ্রি (বা -৯০ ডিগ্রি) ঘুরিয়ে ল্যান্ডস্কেপ ওরিয়েন্টেশনে ক্যামেরা প্রিভিউ ও সেন্সর।

এখানে ছবির বাফার দেওয়া হল:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং ছবি উল্টো
            হয়ে গেছে।

সেন্সর ওরিয়েন্টেশনের জন্য অ্যাডজাস্ট করতে বাফারকে ঘড়ির কাঁটার বিপরীতে ২৭০ ডিগ্রি ঘোরাতে হবে:

ক্যামেরা সেন্সর পোর্ট্রেট ওরিয়েন্টেশনে রেট করা হয়েছে, ছবি পাশে,
            উপরে বাঁদিকে।

তারপর, ডিভাইসের রোটেশন বিবেচনা করার জন্য বাফারটি ঘড়ির কাঁটার বিপরীতে আরও ২৭০ ডিগ্রি ঘোরানো হয়:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং ছবি
            সোজা আছে।

পিছনের ক্যামেরা

পিছনের ক্যামেরার সেন্সর সাধারণত ৯০ ডিগ্রি কোণে থাকে (ডিভাইসের পিছন থেকে দেখলে)। ক্যামেরা প্রিভিউ ওরিয়েন্ট করার সময়, সেন্সর রোটেশনের পরিমাণ অনুযায়ী সেন্সর ইমেজ বাফার ঘড়ির কাঁটার দিকে ঘোরানো হয় (ফ্রন্ট-ফেসিং ক্যামেরার মতো ঘড়ির কাঁটার বিপরীতে নয়) এবং তারপর ডিভাইস রোটেশনের পরিমাণ অনুযায়ী ইমেজ বাফার ঘড়ির কাঁটার বিপরীতে ঘোরানো হয়।

ক্যামেরা প্রিভিউ এবং সেন্সর দুটিই ল্যান্ডস্কেপ ওরিয়েন্টেশনে আছে, কিন্তু
            সেন্সর উল্টো হয়ে আছে।
ছবি ৪. ল্যান্ডস্কেপ ওরিয়েন্টেশনে পিছনের ক্যামেরা সহ ফোন (২৭০ বা -৯০ ডিগ্রি ঘোরানো)।

ছবি ৪-এ ক্যামেরা সেন্সর থেকে পাওয়া ইমেজ বাফার এখানে দেওয়া হল:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং ছবি উল্টো
            হয়ে গেছে।

সেন্সর ওরিয়েন্টেশন অ্যাডজাস্ট করার জন্য বাফারটি ঘড়ির কাঁটার দিকে ৯০ ডিগ্রি ঘোরাতে হবে:

ক্যামেরা সেন্সর পোর্ট্রেট ওরিয়েন্টেশনে রেট করা হয়েছে, ছবি পাশে,
            উপরে বাঁদিকে।

তারপর, ডিভাইস রোটেশনের জন্য বাফারটি ঘড়ির কাঁটার বিপরীতে ২৭০ ডিগ্রি ঘোরানো হয়:

ক্যামেরা সেন্সর ল্যান্ডস্কেপ ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং ছবি
            সোজা আছে।

অ্যাস্পেক্ট রেশিও

ডিভাইসের ওরিয়েন্টেশন পরিবর্তন হলে ডিসপ্লের অ্যাস্পেক্ট রেশিও পরিবর্তন হয়, তবে ফোল্ডেবল ডিভাইস ফোল্ড ও আনফোল্ড করলে, মাল্টি-উইন্ডো পরিবেশে উইন্ডোর সাইজ পরিবর্তন করলে এবং সেকেন্ডারি ডিসপ্লেতে অ্যাপ খুললে ডিসপ্লের অ্যাস্পেক্ট রেশিও পরিবর্তন হয়।

ক্যামেরা সেন্সর ইমেজ বাফারকে অবশ্যই ভিউফাইন্ডার UI এলিমেন্টের ওরিয়েন্টেশন এবং অ্যাস্পেক্ট রেশিওর সাথে মিল রেখে ওরিয়েন্টেড এবং স্কেল করতে হবে কারণ UI ডাইনামিকভাবে ওরিয়েন্টেশন পরিবর্তন করে—ডিভাইস ওরিয়েন্টেশন পরিবর্তন করুক বা না করুক।

নতুন ফর্ম ফ্যাক্টর বা মাল্টি-উইন্ডো বা মাল্টি-ডিসপ্লে পরিবেশে, আপনার অ্যাপ যদি ধরে নেয় যে ক্যামেরা প্রিভিউয়ের ওরিয়েন্টেশন ডিভাইসের ওরিয়েন্টেশনের (পোর্ট্রেট বা ল্যান্ডস্কেপ) মতোই, তাহলে আপনার প্রিভিউ ভুলভাবে ওরিয়েন্টেড, ভুলভাবে স্কেল করা বা দুটিই হতে পারে।

আনফোল্ড করা ফোল্ডেবল ডিভাইসে পোর্ট্রেট ক্যামেরা প্রিভিউ
            পাশে ঘোরানো আছে।
ছবি ৫. ফোল্ডেবল ডিভাইস পোর্ট্রেট থেকে ল্যান্ডস্কেপ অ্যাস্পেক্ট রেশিওতে ট্রানজিশন করে কিন্তু ক্যামেরা সেন্সর পোর্ট্রেট ওরিয়েন্টেশনেই থাকে।

ছবি ৫-এ, অ্যাপ্লিকেশনটি ভুলবশত ধরে নিয়েছে যে ডিভাইসটি ঘড়ির কাঁটার বিপরীতে ৯০ ডিগ্রি ঘোরানো হয়েছে; এবং তাই, অ্যাপটি প্রিভিউটিকে একই পরিমাণে ঘুরিয়েছে।

আনফোল্ড করা ফোল্ডেবল ডিভাইসে ক্যামেরা প্রিভিউ সোজাভাবে দেখাচ্ছে কিন্তু ভুল স্কেলিংয়ের কারণে
            স্কোয়াশ করা হয়েছে।
ছবি ৬. ফোল্ডেবল ডিভাইস পোর্ট্রেট থেকে ল্যান্ডস্কেপ অ্যাস্পেক্ট রেশিওতে ট্রানজিশন করে কিন্তু ক্যামেরা সেন্সর পোর্ট্রেট ওরিয়েন্টেশনেই থাকে।

ছবি ৬-এ, ক্যামেরা প্রিভিউ UI এলিমেন্টের নতুন ডাইমেনশনে ফিট করার জন্য সঠিকভাবে স্কেল করতে, অ্যাপটি ইমেজ বাফারের অ্যাস্পেক্ট রেশিও অ্যাডজাস্ট করেনি।

ফিক্সড-ওরিয়েন্টেশন ক্যামেরা অ্যাপ সাধারণত ফোল্ডেবল এবং ল্যাপটপের মতো অন্যান্য বড় স্ক্রিনের ডিভাইসে সমস্যা সৃষ্টি করে:

ল্যাপটপে ক্যামেরা প্রিভিউ সোজা দেখাচ্ছে কিন্তু অ্যাপ UI পাশে দেখাচ্ছে।
ছবি ৭. ল্যাপটপ কম্পিউটারে ফিক্সড-ওরিয়েন্টেশন পোর্ট্রেট অ্যাপ।

ছবি ৭-এ, ক্যামেরা অ্যাপের UI একপাশে রয়েছে কারণ অ্যাপের ওরিয়েন্টেশন শুধুমাত্র পোর্ট্রেটে সীমাবদ্ধ। ক্যামেরা সেন্সরের সাপেক্ষে ভিউফাইন্ডার ইমেজ সঠিকভাবে ওরিয়েন্টেড।

ইনসেট পোর্ট্রেট মোড

যেসব ক্যামেরা অ্যাপ মাল্টি-উইন্ডো মোড সাপোর্ট করে না (resizeableActivity="false") এবং তাদের ওরিয়েন্টেশন সীমাবদ্ধ করে (screenOrientation="portrait" অথবা screenOrientation="landscape") সেগুলিকে বড় স্ক্রিনের ডিভাইসে ইনসেট পোর্ট্রেট মোডে রাখা যেতে পারে যাতে ক্যামেরা প্রিভিউ সঠিকভাবে ওরিয়েন্ট করা যায়।

ইনসেট পোর্ট্রেট মোড লেটারবক্স (ইনসেট) পোর্ট্রেট-অনলি অ্যাপকে পোর্ট্রেট ওরিয়েন্টেশনে দেখায়, যদিও ডিসপ্লে অ্যাস্পেক্ট রেশিও ল্যান্ডস্কেপ। ডিসপ্লে অ্যাস্পেক্ট রেশিও পোর্ট্রেট হলেও, শুধুমাত্র ল্যান্ডস্কেপ মোডে কাজ করে এমন অ্যাপ ল্যান্ডস্কেপ ওরিয়েন্টেশনে লেটারবক্স করা হয়। ক্যামেরার ছবি অ্যাপ UI-এর সাথে অ্যালাইন করার জন্য ঘোরানো হয়, ক্যামেরার প্রিভিউয়ের অ্যাস্পেক্ট রেশিওর সাথে ম্যাচ করার জন্য ক্রপ করা হয় এবং তারপর প্রিভিউ পূরণ করার জন্য স্কেল করা হয়।

ক্যামেরা ইমেজ সেন্সরের অ্যাস্পেক্ট রেশিও এবং অ্যাপ্লিকেশনের প্রাথমিক অ্যাক্টিভিটির অ্যাস্পেক্ট রেশিও একে অপরের সাথে ম্যাচ না করলে ইনসেট পোর্ট্রেট মোড ট্রিগার করা হয়।

ল্যাপটপে সঠিক পোর্ট্রেট ওরিয়েন্টেশনে ক্যামেরা প্রিভিউ ও অ্যাপ UI।
            পোর্ট্রেট ওরিয়েন্টেশনে ফিট করার জন্য চওড়া প্রিভিউ ছবি স্কেল ও ক্রপ করা
            হয়েছে।
ছবি ৮. ল্যাপটপে ইনসেট পোর্ট্রেট মোডে ফিক্সড-ওরিয়েন্টেশন পোর্ট্রেট অ্যাপ।

ছবি ৮-এ, ল্যাপটপ ডিসপ্লেতে UI সোজা করে দেখানোর জন্য শুধুমাত্র পোর্ট্রেট ক্যামেরা অ্যাপ ঘোরানো হয়েছে। পোর্ট্রেট অ্যাপ ও ল্যান্ডস্কেপ ডিসপ্লের মধ্যে অ্যাস্পেক্ট রেশিওর পার্থক্যের কারণে অ্যাপটি লেটারবক্স করা হয়েছে। অ্যাপের UI রোটেশনের (ইনসেট পোর্ট্রেট মোডের কারণে) জন্য ক্ষতিপূরণ করতে ক্যামেরা প্রিভিউ ছবি রোটেট করা হয়েছে এবং পোর্ট্রেট ওরিয়েন্টেশনে ফিট করার জন্য ছবি ক্রপ ও স্কেল করা হয়েছে, যার ফলে ফিল্ড অফ ভিউ কমে গেছে।

ঘোরান, ক্রপ করুন, স্কেল করুন

ইনসেট পোর্ট্রেট মোড শুধুমাত্র পোর্ট্রেট ক্যামেরা অ্যাপের জন্য ডিসপ্লেতে চালু করা হয় যার ল্যান্ডস্কেপ অ্যাস্পেক্ট রেশিও আছে:

ল্যাপটপে ক্যামেরা প্রিভিউ সোজা দেখাচ্ছে কিন্তু অ্যাপ UI পাশে দেখাচ্ছে।
ছবি ৯. ল্যাপটপে ফিক্সড-ওরিয়েন্টেশন পোর্ট্রেট অ্যাপ।

অ্যাপটি পোর্ট্রেট ওরিয়েন্টেশনে লেটারবক্স করা হয়েছে:

অ্যাপ পোর্ট্রেট ওরিয়েন্টেশনে ঘোরানো হয়েছে এবং লেটারবক্স করা হয়েছে। ছবিটি
            পাশাপাশি, উপরে ডানদিকে আছে।

অ্যাপের রিওরিয়েন্টেশনের জন্য অ্যাডজাস্ট করতে ক্যামেরার ছবি ৯০ ডিগ্রি ঘোরানো হয়েছে:

সেন্সর ইমেজ ৯০ ডিগ্রি ঘোরানো হয়েছে যাতে এটি সোজা দেখায়।

ক্যামেরা প্রিভিউয়ের অ্যাস্পেক্ট রেশিও অনুযায়ী ছবি ক্রপ করা হয়, তারপর প্রিভিউ পূরণ করার জন্য স্কেল করা হয় (ফিল্ড অফ ভিউ কমে যায়):

ক্যামেরা প্রিভিউ পূরণ করার জন্য ক্রপ করা ক্যামেরা ছবি স্কেল করা হয়েছে।

ভাঁজ করা যায় এমন ডিভাইসে, ক্যামেরা সেন্সরের ওরিয়েন্টেশন পোর্ট্রেট হতে পারে যদিও ডিসপ্লের অ্যাস্পেক্ট রেশিও ল্যান্ডস্কেপ:

ক্যামেরা প্রিভিউ ও অ্যাপ UI, চওড়া, আনফোল্ড করা ডিসপ্লের পাশে দেখানো হয়।
ছবি ১০. শুধুমাত্র পোর্ট্রেট ক্যামেরা অ্যাপ সহ আনফোল্ড করা ডিভাইস এবং ক্যামেরা সেন্সর ও ডিসপ্লের বিভিন্ন অ্যাস্পেক্ট রেশিও।

সেন্সর ওরিয়েন্টেশন অ্যাডজাস্ট করার জন্য ক্যামেরা প্রিভিউ রোটেট করা হয়, তাই ভিউফাইন্ডারে ছবি সঠিকভাবে ওরিয়েন্ট করা থাকে, কিন্তু শুধুমাত্র পোর্ট্রেট মোডে কাজ করে এমন অ্যাপ পাশাপাশি থাকে।

ইনসেট পোর্ট্রেট মোডে অ্যাপ ও ক্যামেরা প্রিভিউ সঠিকভাবে ওরিয়েন্ট করতে অ্যাপকে শুধু পোর্ট্রেট ওরিয়েন্টেশনে লেটারবক্স করতে হয়:

ফোল্ডেবল ডিভাইসে ক্যামেরা প্রিভিউ সোজা রেখে পোর্ট্রেট ওরিয়েন্টেশনে
            লেটারবক্স করা অ্যাপ।

API

Android 12 (API লেভেল 31) থেকে অ্যাপগুলিও CaptureRequest ক্লাসের SCALER_ROTATE_AND_CROP প্রপার্টির মাধ্যমে ইনসেট পোর্ট্রেট মোড স্পষ্টভাবে নিয়ন্ত্রণ করতে পারে।

ডিফল্ট ভ্যালু হল SCALER_ROTATE_AND_CROP_AUTO, যা সিস্টেমকে ইনসেট পোর্ট্রেট মোড চালু করতে দেয়। SCALER_ROTATE_AND_CROP_90 হল উপরে বর্ণিত ইনসেট পোর্ট্রেট মোডের আচরণ।

সব ডিভাইসে সব SCALER_ROTATE_AND_CROP ভ্যালু কাজ করে না। সাপোর্ট করা ভ্যালুর তালিকা পেতে, CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES দেখুন।

CameraX

Jetpack CameraX লাইব্রেরি এমন একটি ক্যামেরা ভিউফাইন্ডার তৈরি করে যা সেন্সর ওরিয়েন্টেশন ও ডিভাইস রোটেশনকে একটি সহজ টাস্ক হিসেবে অ্যাকোমোডেট করে।

PreviewView লেআউট এলিমেন্ট ক্যামেরা প্রিভিউ তৈরি করে, সেন্সর ওরিয়েন্টেশন, ডিভাইস রোটেশন ও স্কেলিংয়ের জন্য অটোমেটিক অ্যাডজাস্ট করে। PreviewView, FILL_CENTER স্কেল টাইপ প্রয়োগ করে ক্যামেরা ইমেজের অ্যাস্পেক্ট রেশিও বজায় রাখে, যা ইমেজকে কেন্দ্রে নিয়ে আসে কিন্তু PreviewView-এর ডাইমেনশনের সাথে ম্যাচ করার জন্য এটি ক্রপ করতে পারে। ক্যামেরার ছবি লেটারবক্স করতে, স্কেলের ধরন FIT_CENTER-এ সেট করুন।

PreviewView-এর সাহায্যে ক্যামেরা প্রিভিউ তৈরি করার প্রাথমিক বিষয়গুলি জানতে, প্রিভিউ প্রয়োগ করুন দেখুন।

সম্পূর্ণ স্যাম্পেল ইমপ্লিমেন্টেশনের জন্য, GitHub-এ CameraXBasic রিপোজিটরি দেখুন।

CameraViewfinder

প্রিভিউ ব্যবহারের ক্ষেত্রের মতো, CameraViewfinder লাইব্রেরি ক্যামেরা প্রিভিউ তৈরি করার প্রক্রিয়া সহজ করতে এক সেট টুল প্রদান করে। এটি CameraX Core-এর উপর নির্ভর করে না, তাই আপনি এটিকে আপনার বর্তমান Camera2 কোডবেসে নির্বিঘ্নে ইন্টিগ্রেট করতে পারবেন।

সরাসরি Surface ব্যবহার করার পরিবর্তে, আপনি CameraViewfinder উইজেট ব্যবহার করে Camera2-এর জন্য ক্যামেরা ফিড দেখাতে পারেন।

CameraViewfinder ইন্টার্নালি TextureView বা SurfaceView-এর মধ্যে কোনও একটি ব্যবহার করে ক্যামেরা ফিড দেখায় এবং ভিউফাইন্ডার সঠিকভাবে দেখানোর জন্য প্রয়োজনীয় ট্রান্সফর্মেশন প্রয়োগ করে। এর মধ্যে রয়েছে অ্যাস্পেক্ট রেশিও, স্কেল ও রোটেশন ঠিক করা।

CameraViewfinder অবজেক্ট থেকে সারফেসের অনুরোধ করতে, আপনাকে ViewfinderSurfaceRequest তৈরি করতে হবে।

এই অনুরোধে CameraCharacteristics থেকে সারফেস রেজোলিউশন এবং ক্যামেরা ডিভাইস সংক্রান্ত তথ্যের প্রয়োজনীয়তা রয়েছে।

requestSurfaceAsync() কল করলে সারফেস প্রদানকারীর কাছে অনুরোধ পাঠানো হয়, যা হয় TextureView অথবা SurfaceView এবং Surface-এর ListenableFuture পাওয়া যায়।

markSurfaceSafeToRelease() কল করা হলে সারফেস প্রোভাইডারকে জানানো হয় যে সারফেসের প্রয়োজন নেই এবং সম্পর্কিত রিসোর্স রিলিজ করা যেতে পারে।

Kotlin

fun startCamera(){
    val previewResolution = Size(width, height)
    val viewfinderSurfaceRequest =
        ViewfinderSurfaceRequest(previewResolution, characteristics)
    val surfaceListenableFuture =
        cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

    Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
        override fun onSuccess(surface: Surface) {
            /* create a CaptureSession using this surface as usual */
        }
        override fun onFailure(t: Throwable) { /* something went wrong */}
    }, ContextCompat.getMainExecutor(context))
}

জাভা

    void startCamera(){
        Size previewResolution = new Size(width, height);
        ViewfinderSurfaceRequest viewfinderSurfaceRequest =
                new ViewfinderSurfaceRequest(previewResolution, characteristics);
        ListenableFuture<Surface> surfaceListenableFuture =
                cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest);

        Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() {
            @Override
            public void onSuccess(Surface result) {
                /* create a CaptureSession using this surface as usual */
            }
            @Override public void onFailure(Throwable t) { /* something went wrong */}
        },  ContextCompat.getMainExecutor(context));
    }

SurfaceView

প্রিভিউতে প্রসেসিংয়ের প্রয়োজন না হলে এবং সেটি অ্যানিমেটেড না হলে, ক্যামেরা প্রিভিউ তৈরি করার জন্য SurfaceView হল একটি সহজ পদ্ধতি।

SurfaceView ডিসপ্লে ওরিয়েন্টেশনের সাথে ম্যাচ করার জন্য ক্যামেরা সেন্সর ইমেজ বাফার অটোমেটিক রোটেট করে, সেন্সর ওরিয়েন্টেশন ও ডিভাইস রোটেশন, দুটির জন্যই এটি প্রযোজ্য। তবে, অ্যাস্পেক্ট রেশিও বিবেচনা না করেই SurfaceView ডাইমেনশনে ফিট করার জন্য ইমেজ বাফার স্কেল করা হয়।

আপনাকে অবশ্যই নিশ্চিত করতে হবে যে ইমেজ বাফারের অ্যাসপেক্ট রেশিও SurfaceView-এর অ্যাসপেক্ট রেশিওর সাথে মেলে, যা আপনি কম্পোনেন্টের onMeasure() পদ্ধতিতে SurfaceView-এর কন্টেন্ট স্কেল করে করতে পারেন:

(computeRelativeRotation() সোর্স কোডটি নিচে রিলেটিভ রোটেশন-এ আছে।)

Kotlin

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val width = MeasureSpec.getSize(widthMeasureSpec)
    val height = MeasureSpec.getSize(heightMeasureSpec)

    val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees)

    if (previewWidth > 0f && previewHeight > 0f) {
        /* Scale factor required to scale the preview to its original size on the x-axis. */
        val scaleX =
            if (relativeRotation % 180 == 0) {
                width.toFloat() / previewWidth
            } else {
                width.toFloat() / previewHeight
            }
        /* Scale factor required to scale the preview to its original size on the y-axis. */
        val scaleY =
            if (relativeRotation % 180 == 0) {
                height.toFloat() / previewHeight
            } else {
                height.toFloat() / previewWidth
            }

        /* Scale factor required to fit the preview to the SurfaceView size. */
        val finalScale = min(scaleX, scaleY)

        setScaleX(1 / scaleX * finalScale)
        setScaleY(1 / scaleY * finalScale)
    }
    setMeasuredDimension(width, height)
}

জাভা

@Override
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees);

    if (previewWidth > 0f && previewHeight > 0f) {

        /* Scale factor required to scale the preview to its original size on the x-axis. */
        float scaleX = (relativeRotation % 180 == 0)
                       ? (float) width / previewWidth
                       : (float) width / previewHeight;

        /* Scale factor required to scale the preview to its original size on the y-axis. */
        float scaleY = (relativeRotation % 180 == 0)
                       ? (float) height / previewHeight
                       : (float) height / previewWidth;

        /* Scale factor required to fit the preview to the SurfaceView size. */
        float finalScale = Math.min(scaleX, scaleY);

        setScaleX(1 / scaleX * finalScale);
        setScaleY(1 / scaleY * finalScale);
    }
    setMeasuredDimension(width, height);
}

ক্যামেরা প্রিভিউ হিসেবে SurfaceView প্রয়োগ করার ব্যাপারে আরও জানতে, ক্যামেরা ওরিয়েন্টেশন দেখুন।

TextureView

TextureView-এর পারফর্ম্যান্স SurfaceView-এর থেকে কম—এবং এতে বেশি কাজ করতে হয়—তবে TextureView আপনাকে ক্যামেরা প্রিভিউয়ের উপর সর্বাধিক নিয়ন্ত্রণ দেয়।

TextureView সেন্সর ওরিয়েন্টেশনের উপর ভিত্তি করে সেন্সর ইমেজ বাফার রোটেট করে, কিন্তু ডিভাইস রোটেশন বা প্রিভিউ স্কেলিং ম্যানেজ করে না।

স্কেলিং ও রোটেশন ম্যাট্রিক্স ট্রান্সফর্মেশনে এনকোড করা যেতে পারে। কীভাবে সঠিকভাবে TextureView স্কেল ও রোটেট করতে হয় তা জানতে, আপনার ক্যামেরা অ্যাপে রিসাইজ করা যায় এমন সারফেস সাপোর্ট করুন নিবন্ধটি দেখুন

আপেক্ষিক রোটেশন

ক্যামেরা সেন্সরের আপেক্ষিক রোটেশন হল ক্যামেরা সেন্সর আউটপুটকে ডিভাইসের ওরিয়েন্টেশনের সাথে অ্যালাইন করার জন্য প্রয়োজনীয় রোটেশনের পরিমাণ।

প্রিভিউ ছবির জন্য x এবং y স্কেলিং ফ্যাক্টর নির্ধারণ করতে SurfaceView এবং TextureView-এর মতো কম্পোনেন্ট দ্বারা আপেক্ষিক রোটেশন ব্যবহার করা হয়। এছাড়াও, এটি সেন্সর ইমেজ বাফারের রোটেশন নির্দিষ্ট করতে ব্যবহার করা হয়।

CameraCharacteristics এবং Surface ক্লাস ক্যামেরা সেন্সরের আপেক্ষিক রোটেশন গণনা করতে সাহায্য করে:

Kotlin

/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public fun computeRelativeRotation(
    characteristics: CameraCharacteristics,
    surfaceRotationDegrees: Int
): Int {
    val sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    // Reverse device orientation for back-facing cameras.
    val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT
    ) 1 else -1

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360
}

জাভা

/**
 * Computes rotation required to transform the camera sensor output orientation to the
 * device's current orientation in degrees.
 *
 * @param characteristics The CameraCharacteristics to query for the sensor orientation.
 * @param surfaceRotationDegrees The current device orientation as a Surface constant.
 * @return Relative rotation of the camera sensor output.
 */
public int computeRelativeRotation(
    CameraCharacteristics characteristics,
    int surfaceRotationDegrees
){
    Integer sensorOrientationDegrees =
        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

    // Reverse device orientation for back-facing cameras.
    int sign = characteristics.get(CameraCharacteristics.LENS_FACING) ==
        CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1;

    // Calculate desired orientation relative to camera orientation to make
    // the image upright relative to the device orientation.
    return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360;
}

উইন্ডো মেট্রিক

ক্যামেরা ভিউফাইন্ডারের ডাইমেনশন নির্ধারণ করতে স্ক্রিনের সাইজ ব্যবহার করা উচিত নয়; ক্যামেরা অ্যাপ স্ক্রিনের কোনও অংশে চলতে পারে, মোবাইল ডিভাইসে মাল্টি-উইন্ডো মোডে অথবা ChromeOS-এ ফ্রি-ফর্ম মোডে।

WindowManager#getCurrentWindowMetrics() (API লেভেল ৩০-এ যোগ করা হয়েছে) স্ক্রিনের সাইজের পরিবর্তে অ্যাপ্লিকেশন উইন্ডোর সাইজ রিটার্ন করে। Jetpack WindowManager লাইব্রেরি পদ্ধতি WindowMetricsCalculator#computeCurrentWindowMetrics() এবং WindowInfoTracker#currentWindowMetrics() API লেভেল ১৪-এ ব্যাকওয়ার্ড কম্প্যাটিবিলিটির সাথে একই ধরনের সহায়তা প্রদান করে।

১৮০ ডিগ্রিতে ঘোরানো

ডিভাইস ১৮০ ডিগ্রি ঘোরানো হলে (যেমন, স্বাভাবিক ওরিয়েন্টেশন থেকে স্বাভাবিক ওরিয়েন্টেশন উল্টো করে দেওয়া) onConfigurationChanged() কলব্যাক ট্রিগার হয় না। এর ফলে, ক্যামেরার প্রিভিউ উল্টো হয়ে যেতে পারে।

১৮০ ডিগ্রি রোটেশন শনাক্ত করতে, DisplayListener প্রয়োগ করুন এবং onDisplayChanged() কলব্যাকে Display#getRotation() কল করে ডিভাইস রোটেশন চেক করুন।

এক্সক্লুসিভ রিসোর্স

Android 10-এর আগে, মাল্টি-উইন্ডো এনভায়রনমেন্টে শুধুমাত্র সবচেয়ে উপরে থাকা দৃশ্যমান অ্যাক্টিভিটি RESUMED স্ট্যাটাসে থাকত। এটি ব্যবহারকারীদের জন্য বিভ্রান্তিকর ছিল কারণ সিস্টেমটি কোন অ্যাক্টিভিটি আবার শুরু করা হয়েছে তার কোনও ইঙ্গিত দেয়নি।

Android 10 (API লেভেল ২৯) মাল্টি-রিজিউম চালু করেছে যেখানে সব দৃশ্যমান অ্যাক্টিভিটি RESUMED স্টেটে থাকে। দৃশ্যমান অ্যাক্টিভিটি এখনও PAUSED স্টেটে প্রবেশ করতে পারে, যেমন, কোনও স্বচ্ছ অ্যাক্টিভিটি যদি অ্যাক্টিভিটির উপরে থাকে বা অ্যাক্টিভিটি ফোকাস করা না যায়, যেমন ছবির-মধ্যে-ছবি মোডে (ছবির-মধ্যে-ছবি সংক্রান্ত সহায়তা দেখুন)।

API লেভেল ২৯ বা তার পরবর্তী যেকোনও ভার্সনে ক্যামেরা, মাইক্রোফোন বা যেকোনও এক্সক্লুসিভ বা সিঙ্গলটন রিসোর্স ব্যবহার করা অ্যাপ্লিকেশনকে অবশ্যই মাল্টি-রিজিউম সাপোর্ট করতে হবে। যেমন, তিনটি রিজুম করা অ্যাক্টিভিটি যদি ক্যামেরা ব্যবহার করতে চায়, তাহলে শুধুমাত্র একটিই এই এক্সক্লুসিভ রিসোর্স অ্যাক্সেস করতে পারবে। আরও বেশি অগ্রাধিকারপ্রাপ্ত অ্যাক্টিভিটির মাধ্যমে ক্যামেরার প্রি-এমপ্টিভ অ্যাক্সেস সম্পর্কে সচেতন থাকতে, প্রতিটি অ্যাক্টিভিটিকে অবশ্যই একটি onDisconnected() কলব্যাক প্রয়োগ করতে হবে।

আরও তথ্যের জন্য, মাল্টি-রিজিউম দেখুন।

অতিরিক্ত রিসোর্স