ज़्यादातर ब्राउज़र, उपयोगकर्ता के कैमरे का ऐक्सेस पा सकते हैं.
अब कई ब्राउज़र, उपयोगकर्ता से वीडियो और ऑडियो इनपुट ऐक्सेस कर सकते हैं. हालांकि, ब्राउज़र के हिसाब से यह पूरी तरह डाइनैमिक और इनलाइन अनुभव हो सकता है. इसके अलावा, इसे उपयोगकर्ता के डिवाइस पर मौजूद किसी दूसरे ऐप्लिकेशन को भी सौंपा जा सकता है. इसके अलावा, हर डिवाइस में कैमरा भी नहीं होता. तो, ऐसा अनुभव कैसे बनाया जा सकता है जिसमें उपयोगकर्ता की जनरेट की गई इमेज का इस्तेमाल किया जा सके और वह हर जगह ठीक से काम करे?
आसानी से और धीरे-धीरे शुरू करें
अगर आपको अपने अनुभव को बेहतर बनाना है, तो आपको किसी ऐसे टूल से शुरुआत करनी होगी जो हर जगह काम करता हो. सबसे आसान तरीका यह है कि उपयोगकर्ता से पहले से रिकॉर्ड की गई फ़ाइल मांगी जाए.
यूआरएल मांगना
यह सबसे अच्छा विकल्प है, लेकिन यह पूरी तरह से संतोषजनक नहीं है. उपयोगकर्ता से कोई यूआरएल पाएं और फिर उसका इस्तेमाल करें. सिर्फ़ इमेज दिखाने के लिए, यह हर जगह काम करता है. img
एलिमेंट बनाएं और src
सेट करें.
हालांकि, अगर आपको इमेज में किसी भी तरह का बदलाव करना है, तो चीज़ें थोड़ी मुश्किल हो जाती हैं. CORS, असली पिक्सल को ऐक्सेस करने से रोकता है. ऐसा तब तक होता है, जब तक सर्वर सही हेडर सेट नहीं करता और आपने इमेज को क्रॉसओरिजिन के तौर पर मार्क नहीं किया. इस समस्या को हल करने का एक ही तरीका है, प्रॉक्सी सर्वर चलाना.
फ़ाइल इनपुट
इसके अलावा, किसी सामान्य फ़ाइल इनपुट एलिमेंट का इस्तेमाल भी किया जा सकता है. इसमें accept
फ़िल्टर भी शामिल है, जो यह दिखाता है कि आपको सिर्फ़ इमेज फ़ाइलें चाहिए.
<input type="file" accept="image/*" />
यह तरीका सभी प्लैटफ़ॉर्म पर काम करता है. डेस्कटॉप पर, यह उपयोगकर्ता को फ़ाइल सिस्टम से इमेज फ़ाइल अपलोड करने के लिए कहेगा. iOS और Android पर Chrome और Safari में, इस तरीके से उपयोगकर्ता को यह चुनने का विकल्प मिलेगा कि इमेज कैप्चर करने के लिए किस ऐप्लिकेशन का इस्तेमाल करना है. इसमें, सीधे कैमरे से फ़ोटो लेने या किसी मौजूदा इमेज फ़ाइल को चुनने का विकल्प भी शामिल है.
इसके बाद, डेटा को <form>
से अटैच किया जा सकता है या JavaScript की मदद से डेटा में बदलाव किया जा सकता है. इसके लिए, इनपुट एलिमेंट पर onchange
इवेंट को सुनें और फिर इवेंट target
की files
प्रॉपर्टी पढ़ें.
<input type="file" accept="image/*" id="file-input" />
<script>
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', (e) =>
doSomethingWithFiles(e.target.files),
);
</script>
files
प्रॉपर्टी एक FileList
ऑब्जेक्ट है. हम इस बारे में बाद में ज़्यादा बात करेंगे.
आपके पास एलिमेंट में capture
एट्रिब्यूट को जोड़ने का विकल्प भी है. इससे ब्राउज़र को यह पता चलता है कि आपको कैमरे से इमेज चाहिए.
<input type="file" accept="image/*" capture />
<input type="file" accept="image/*" capture="user" />
<input type="file" accept="image/*" capture="environment" />
वैल्यू के बिना capture
एट्रिब्यूट जोड़ने पर, ब्राउज़र यह तय करता है कि किस कैमरे का इस्तेमाल करना है. वहीं, "user"
और "environment"
वैल्यू से ब्राउज़र को पता चलता है कि उसे फ़्रंट और रियर कैमरे में से किसका इस्तेमाल करना है.
capture
एट्रिब्यूट, Android और iOS पर काम करता है. हालांकि, डेस्कटॉप पर इसे अनदेखा कर दिया जाता है. हालांकि, ध्यान रखें कि Android पर ऐसा करने का मतलब है कि उपयोगकर्ता के पास अब कोई मौजूदा फ़ोटो चुनने का विकल्प नहीं होगा. इसके बजाय, सिस्टम कैमरा ऐप्लिकेशन सीधे चालू हो जाएगा.
खींचें और छोड़ें
अगर आपने पहले से ही फ़ाइल अपलोड करने की सुविधा जोड़ी है, तो उपयोगकर्ता अनुभव को बेहतर बनाने के लिए, यहां दिए गए कुछ आसान तरीके अपनाएं.
पहला तरीका, अपने पेज पर ड्रॉप टारगेट जोड़ना है. इससे उपयोगकर्ता, डेस्कटॉप या किसी दूसरे ऐप्लिकेशन से फ़ाइल को खींचकर छोड़ सकता है.
<div id="target">You can drag an image file here</div>
<script>
const target = document.getElementById('target');
target.addEventListener('drop', (e) => {
e.stopPropagation();
e.preventDefault();
doSomethingWithFiles(e.dataTransfer.files);
});
target.addEventListener('dragover', (e) => {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
</script>
फ़ाइल इनपुट की तरह ही, आपको drop
इवेंट की dataTransfer.files
प्रॉपर्टी से FileList
ऑब्जेक्ट मिल सकता है;
dragover
इवेंट हैंडलर की मदद से, उपयोगकर्ता को यह सिग्नल दिया जा सकता है कि फ़ाइल को ड्रॉप करने पर क्या होगा. इसके लिए, dropEffect
प्रॉपर्टी का इस्तेमाल करें.
खींचकर छोड़ने की सुविधा का इस्तेमाल, लंबे समय से किया जा रहा है. यह सुविधा, ज़्यादातर ब्राउज़र पर काम करती है.
क्लिपबोर्ड से चिपकाना
क्लिपबोर्ड से भी किसी मौजूदा इमेज फ़ाइल को पाया जा सकता है. इसका कोड बहुत आसान है, लेकिन उपयोगकर्ता अनुभव को सही बनाना थोड़ा मुश्किल है.
<textarea id="target">Paste an image here</textarea>
<script>
const target = document.getElementById('target');
target.addEventListener('paste', (e) => {
e.preventDefault();
doSomethingWithFiles(e.clipboardData.files);
});
</script>
(e.clipboardData.files
एक और FileList
ऑब्जेक्ट है.)
क्लिपबोर्ड एपीआई का सबसे मुश्किल हिस्सा यह है कि सभी ब्राउज़र पर काम करने के लिए, टारगेट एलिमेंट को चुना जा सकता हो और उसमें बदलाव किया जा सकता हो. यहां <textarea>
और <input type="text">
, दोनों एट्रिब्यूट काम के हैं. साथ ही, contenteditable
एट्रिब्यूट वाले एलिमेंट भी काम के हैं. हालांकि, इन्हें टेक्स्ट में बदलाव करने के लिए भी डिज़ाइन किया गया है.
अगर आपको उपयोगकर्ता को टेक्स्ट डालने की अनुमति नहीं देनी है, तो इसे आसानी से काम कराने में समस्या आ सकती है. छिपे हुए इनपुट का इस्तेमाल करना, जैसे कि किसी दूसरे एलिमेंट पर क्लिक करने पर चुना जाना, ऐक्सेस करने की सुविधा को बनाए रखने के लिए मुश्किल हो सकता है.
FileList ऑब्जेक्ट को हैंडल करना
ऊपर बताए गए ज़्यादातर तरीकों से FileList
मिलता है. इसलिए, हमें इस बारे में थोड़ी बात करनी चाहिए कि FileList
क्या है.
FileList
, Array
से मिलता-जुलता है. इसमें अंकों वाली कुंजियां और length
प्रॉपर्टी होती है, लेकिन यह असल में कोई ऐरे नहीं है. इसमें forEach()
या pop()
जैसे ऐरे तरीके नहीं हैं. साथ ही, इसे दोहराया नहीं जा सकता.
Array.from(fileList)
का इस्तेमाल करके, रीयल ऐरे भी बनाया जा सकता है.
FileList
की एंट्री, File
ऑब्जेक्ट हैं. ये Blob
ऑब्जेक्ट के जैसे ही होते हैं, सिवाय इसके कि इनमें name
और lastModified
रीड-ओनली प्रॉपर्टी होती हैं.
<img id="output" />
<script>
const output = document.getElementById('output');
function doSomethingWithFiles(fileList) {
let file = null;
for (let i = 0; i < fileList.length; i++) {
if (fileList[i].type.match(/^image\//)) {
file = fileList[i];
break;
}
}
if (file !== null) {
output.src = URL.createObjectURL(file);
}
}
</script>
इस उदाहरण में, इमेज MIME टाइप वाली पहली फ़ाइल ढूंढी जाती है. हालांकि, यह एक साथ कई इमेज चुनने/चिपकाने/ड्रॉप करने की सुविधा भी देती है.
फ़ाइल का ऐक्सेस मिलने के बाद, उसमें अपनी पसंद के मुताबिक बदलाव किए जा सकते हैं. उदाहरण के लिए, ये काम किए जा सकते हैं:
- इसे
<canvas>
एलिमेंट में ड्रॉ करें, ताकि आप उसमें बदलाव कर सकें - उसे उपयोगकर्ता के डिवाइस पर डाउनलोड करें
fetch()
की मदद से, इसे किसी सर्वर पर अपलोड करें
कैमरे को इंटरैक्टिव तरीके से ऐक्सेस करना
अब जब आपने बुनियादी बातें जान ली हैं, तो इसे बेहतर बनाने का समय आ गया है!
आधुनिक ब्राउज़र, कैमरों का सीधा ऐक्सेस पा सकते हैं. इससे, वेब पेज के साथ पूरी तरह से इंटिग्रेट किए गए अनुभव बनाए जा सकते हैं, ताकि उपयोगकर्ता को ब्राउज़र से कभी बाहर नहीं जाना पड़े.
कैमरे का ऐक्सेस पाना
getUserMedia()
नाम के WebRTC स्पेसिफ़िकेशन में मौजूद एपीआई का इस्तेमाल करके, सीधे तौर पर कैमरे और माइक्रोफ़ोन को ऐक्सेस किया जा सकता है. इससे उपयोगकर्ता को, कनेक्ट किए गए माइक्रोफ़ोन और कैमरों का ऐक्सेस देने के लिए कहा जाएगा.
getUserMedia()
के लिए सहायता काफ़ी अच्छी है, लेकिन यह सुविधा अभी हर जगह उपलब्ध नहीं है. खास तौर पर, यह सुविधा Safari 10 या उससे पहले के वर्शन में उपलब्ध नहीं है. यह सुविधा, लेख लिखने के समय तक Safari का सबसे नया स्टेबल वर्शन है.
हालांकि, Apple ने एलान किया है कि यह Safari 11 में उपलब्ध होगा.
हालांकि, सहायता टीम को ढूंढना बहुत आसान है.
const supported = 'mediaDevices' in navigator;
getUserMedia()
को कॉल करते समय, आपको एक ऑब्जेक्ट पास करना होगा. इससे यह पता चलता है कि आपको किस तरह का मीडिया चाहिए. इन विकल्पों को पाबंदियां कहा जाता है. स्ट्रीम करने के लिए कई चीज़ों का ध्यान रखना ज़रूरी होता है. जैसे, आपको सामने वाला कैमरा चाहिए या पीछे वाला, आपको ऑडियो चाहिए या नहीं, और स्ट्रीम के लिए आपका पसंदीदा रिज़ॉल्यूशन क्या है.
हालांकि, कैमरे से डेटा पाने के लिए, आपको सिर्फ़ एक शर्त पूरी करनी होगी और वह है video: true
.
अगर एपीआई सही से जुड़ जाता है, तो वह एक MediaStream
दिखाएगा. इसमें कैमरे का डेटा शामिल होगा. इसके बाद, इसे <video>
एलिमेंट से अटैच करके रीयल टाइम में झलक देखी जा सकती है या स्नैपशॉट पाने के लिए, इसे <canvas>
से अटैच किया जा सकता है.
<video id="player" controls playsinline autoplay></video>
<script>
const player = document.getElementById('player');
const constraints = {
video: true,
};
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
player.srcObject = stream;
});
</script>
यह अपने-आप इतना काम का नहीं है. सिर्फ़ वीडियो का डेटा लिया जा सकता है और उसे फिर से चलाया जा सकता है. अगर आपको इमेज चाहिए, तो आपको थोड़ा और काम करना होगा.
स्नैपशॉट लेना
इमेज पाने के लिए, वीडियो से कैनवस पर फ़्रेम बनाना सबसे अच्छा विकल्प है.
Web Audio API के उलट, वेब पर वीडियो के लिए कोई खास स्ट्रीम प्रोसेसिंग एपीआई नहीं है. इसलिए, उपयोगकर्ता के कैमरे से स्नैपशॉट लेने के लिए, आपको कुछ हैकिंग की रणनीतियों का इस्तेमाल करना होगा.
यह प्रोसेस इस तरह होती है:
- कैनवस ऑब्जेक्ट बनाएं, जो कैमरे से फ़्रेम को होल्ड करेगा
- कैमरे की स्ट्रीम का ऐक्सेस पाना
- इसे किसी वीडियो एलिमेंट से अटैच करें
- जब आपको कोई सटीक फ़्रेम कैप्चर करना हो, तो
drawImage()
का इस्तेमाल करके वीडियो एलिमेंट का डेटा, कैनवस ऑब्जेक्ट में जोड़ें.
<video id="player" controls playsinline autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width="320" height="240"></canvas>
<script>
const player = document.getElementById('player');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const captureButton = document.getElementById('capture');
const constraints = {
video: true,
};
captureButton.addEventListener('click', () => {
// Draw the video frame to the canvas.
context.drawImage(player, 0, 0, canvas.width, canvas.height);
});
// Attach the video stream to the video element and autoplay.
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
player.srcObject = stream;
});
</script>
कैमरे से कैप्चर किया गया डेटा, Canvas में सेव होने के बाद उससे कई काम किए जा सकते हैं. इसके लिए, ये काम किए जा सकते हैं:
- इसे सीधे सर्वर पर अपलोड करें
- इसे डिवाइस पर सेव करना
- इमेज में मज़ेदार इफ़ेक्ट लागू करना
सलाह
ज़रूरत न होने पर, कैमरे से स्ट्रीमिंग बंद करना
जब आपको कैमरे की ज़रूरत न हो, तो उसका इस्तेमाल बंद कर देना अच्छा होता है. इससे बैटरी और प्रोसेसिंग पावर बचेगी ही, साथ ही, उपयोगकर्ताओं को आपके ऐप्लिकेशन पर भरोसा भी बढ़ेगा.
कैमरे का ऐक्सेस बंद करने के लिए, getUserMedia()
से मिली स्ट्रीम के लिए, हर वीडियो ट्रैक पर stop()
को कॉल करें.
<video id="player" controls playsinline autoplay></video>
<button id="capture">Capture</button>
<canvas id="canvas" width="320" height="240"></canvas>
<script>
const player = document.getElementById('player');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const captureButton = document.getElementById('capture');
const constraints = {
video: true,
};
captureButton.addEventListener('click', () => {
context.drawImage(player, 0, 0, canvas.width, canvas.height);
// Stop all video streams.
player.srcObject.getVideoTracks().forEach(track => track.stop());
});
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
// Attach the video stream to the video element and autoplay.
player.srcObject = stream;
});
</script>
कैमरे का ज़िम्मेदारी से इस्तेमाल करने के लिए अनुमति मांगना
अगर उपयोगकर्ता ने पहले कभी आपकी साइट को कैमरे का ऐक्सेस नहीं दिया है, तो getUserMedia()
को कॉल करने के तुरंत बाद, ब्राउज़र उपयोगकर्ता को आपकी साइट को कैमरे का ऐक्सेस देने के लिए कहेगा.
उपयोगकर्ताओं को अपनी मशीन पर बेहतरीन डिवाइसों का ऐक्सेस पाने के लिए प्रॉम्प्ट मिलने से नफ़रत होती है. वे अक्सर इस अनुरोध को ब्लॉक कर देते हैं. इसके अलावा, अगर उन्हें प्रॉम्प्ट के संदर्भ की जानकारी नहीं होती है, तो वे उसे अनदेखा कर देते हैं. हमारा सुझाव है कि जब ज़रूरत हो, तब ही कैमरे का ऐक्सेस मांगें. उपयोगकर्ता से ऐक्सेस मिलने के बाद, उससे फिर से अनुमति नहीं मांगी जाएगी. हालांकि, अगर उपयोगकर्ता ऐक्सेस देने से मना कर देता है, तो आपको फिर से ऐक्सेस नहीं मिल सकता. ऐसा तब तक होगा, जब तक वह मैन्युअल तरीके से कैमरे की अनुमति की सेटिंग में बदलाव नहीं करता.
इनके साथ काम करता है
मोबाइल और डेस्कटॉप ब्राउज़र पर लागू करने के बारे में ज़्यादा जानकारी:
हमारा सुझाव है कि आप adapter.js शिम का इस्तेमाल करें, ताकि ऐप्लिकेशन को WebRTC स्पेसिफ़िकेशन में हुए बदलावों और प्रीफ़िक्स के अंतर से बचाया जा सके.