Pencere içinde pencere özelliğini kullanarak video izleme

François Beaufort
François Beaufort

Pencere İçinde Pencere (PIP) kullanıcıların, videoları kayan bir pencerede (her zaman diğer pencerelerin üstünde) izlemesine olanak tanır. Böylece kullanıcılar, diğer siteler veya uygulamalarla etkileşimde bulunurken izledikleri içeriğe göz atabilir.

Pencere İçinde Pencere Web API'si ile web sitenizde video öğelerini başlatabilir ve kontrol edebilirsiniz. Resmi Pencere İçinde Pencere örneğimizde deneyin.

Arka plan

Safari, Eylül 2016'da macOS Sierra'daki bir WebKit API'si aracılığıyla Pencere İçinde Pencere desteğini ekledi. Altı ay sonra Chrome, yerel bir Android API'si kullanan Android O sürümüyle birlikte mobil cihazlarda Pencere İçinde Pencere videoyu otomatik olarak oynattı. Altı ay sonra, Safari'ninkiyle uyumlu olan ve web geliştiricilerinin Pencere İçinde Pencere deneyimi özelliğini eksiksiz bir şekilde oluşturmasına ve kontrol etmesine olanak tanıyan bir Web API'si oluşturup bu özelliği standartlaştırmak üzere amacımızı duyurduk. İşte buradayız!

Kodu inceleyin

Pencere içinde pencere moduna gir

Basit bir şekilde, bir video öğesiyle ve kullanıcının düğmeyle etkileşime geçebileceği bir yolla başlayalım.

<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>

Pencere İçinde Pencere isteğini yalnızca bir kullanıcı hareketine yanıt olarak ister ve hiçbir zaman videoElement.play() tarafından döndürülen söz konusu olmaz. Bunun nedeni, taahhütlerin henüz kullanıcı hareketlerini yaymamasıdır. Bunun yerine, aşağıda gösterildiği gibi pipButtonElement üzerindeki bir tıklama işleyicide requestPictureInPicture() çağrısı yapın. Bir kullanıcı iki kez tıklarsa neler olacağını ele almak sizin sorumluluğunuzdadır.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Sözü yerine getirildiğinde Chrome, videoyu kullanıcının hareket edebileceği ve diğer pencerelerin üzerine yerleştirebileceği küçük bir pencereye sıkıştırır.

Hepsi bu kadar. Tebrikler! Okumayı bırakıp güzel bir tatili hazırlamaya devam edebilirsiniz. Ne yazık ki bu her zaman mümkün değildir. Vaat, aşağıdaki nedenlerden herhangi biri dolayısıyla reddedilebilir:

  • Pencere içinde pencere özelliği sistem tarafından desteklenmiyor.
  • Kısıtlayıcı izin politikası nedeniyle Document'ın Pencere İçinde Pencere özelliğini kullanmasına izin verilmiyor.
  • Video meta verileri henüz yüklenmedi (videoElement.readyState === 0).
  • Video dosyası yalnızca ses dosyasıdır.
  • Video öğesinde yeni disablePictureInPicture özelliği bulunur.
  • Çağrı, bir kullanıcı hareketi etkinlik işleyicisinde (ör. düğmeyi tıklama) yapılmamış. Chrome 74'ten itibaren bu işlem yalnızca Pencere İçinde Pencere içinde zaten bir öğe yoksa geçerlidir.

Aşağıdaki Özellik desteği bölümünde, bir düğmenin bu kısıtlamalara dayalı olarak nasıl etkinleştirileceği/devre dışı bırakılacağı gösterilmektedir.

Bu olası hataları yakalamak ve kullanıcıya neler olduğunu bildirmek için bir try...catch bloğu ekleyelim.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  try {
    await videoElement.requestPictureInPicture();
  } catch (error) {
    // TODO: Show error message to user.
  } finally {
    pipButtonElement.disabled = false;
  }
});

Video öğesi, Pencere İçinde Pencere modunda olsun veya olmasın aynı şekilde davranır: Etkinlikler tetiklenir ve arama yöntemleri çalışır. Pencere İçinde Pencere penceresindeki durum değişikliklerini (oynatma, duraklatma, arama gibi) yansıtır ve JavaScript'te durumu programlı bir şekilde değiştirmek de mümkündür.

Pencere İçinde Pencere modundan çık

Şimdi, düğmemizi Pencere İçinde Pencere moduna girme ve bu pencereden çıkma düğmesini etkinleştirelim. Öncelikle, salt okunur nesnenin document.pictureInPictureElement bizim video öğemiz olup olmadığını kontrol etmemiz gerekir. Değilse yukarıdaki gibi Pencere İçinde Pencerede pencere moduna girmek için bir istek göndeririz. Aksi takdirde, document.exitPictureInPicture() numaralı telefonu arayarak ayrılmanızı isteriz. Bu durumda video tekrar ilk sekmede görünür. Bu yöntemin bir vaat getirdiğini de unutmayın.

    ...
    try {
      if (videoElement !== document.pictureInPictureElement) {
        await videoElement.requestPictureInPicture();
      } else {
        await document.exitPictureInPicture();
      }
    }
    ...

Pencere içinde pencere etkinliklerini dinleme

İşletim sistemleri genellikle Pencere İçinde Pencere özelliğini tek bir pencereyle sınırlandırır. Dolayısıyla, Chrome'un uygulaması bu yöntemi izler. Yani, kullanıcılar aynı anda yalnızca bir tane Pencere İçinde Pencere videosu oynatabilir. Kullanıcıların siz istemediğiniz zamanlarda bile Pencere İçinde Pencere modundan çıkmalarını beklemelisiniz.

Yeni enterpictureinpicture ve leavepictureinpicture etkinlik işleyicileri, deneyimi kullanıcılar için özelleştirmemize olanak tanıyor. Bu, bir video kataloğuna göz atmaktan canlı yayın sohbetine kadar pek çok şey olabilir.

videoElement.addEventListener('enterpictureinpicture', function (event) {
  // Video entered Picture-in-Picture.
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  // Video left Picture-in-Picture.
  // User may have played a Picture-in-Picture video from a different page.
});

Pencere içinde pencere penceresini özelleştirme

Chrome 74, Medya Oturumu API'sini kullanarak kontrol edebileceğiniz Pencere İçinde Pencere penceresinde oynat/duraklat, önceki parça ve sonraki parça düğmelerini destekler.

Pencere İçinde Pencere penceresinde medya oynatma kontrolleri
Şekil 1. Pencere İçinde Pencere penceresinde medya oynatma kontrolleri

Videoda MediaStream nesneleri (ör. getUserMedia(), getDisplayMedia(), canvas.captureStream()) oynatılmıyorsa veya video MediaSource süresi +Infinity (ör. canlı feed) olarak ayarlanmamışsa pencere içinde pencere penceresinde varsayılan olarak her zaman bir oynat/duraklat düğmesi gösterilir. Oynat/duraklat düğmesinin her zaman görünür olduğundan emin olmak amacıyla hem "Oynat" hem de "Duraklat" medya etkinlikleri için bazı Medya Oturumu işlem işleyicilerini aşağıda gösterildiği gibi ayarlayın.

// Show a play/pause button in the Picture-in-Picture window
navigator.mediaSession.setActionHandler('play', function () {
  // User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function () {
  // User clicked "Pause" button.
});

"Önceki Parça" ve "Sonraki parça" pencere denetimlerinin gösterilmesi benzerdir. Bunların Medya Oturumu işlem işleyicileri ayarlanması, bunları Pencere İçinde Pencere penceresinde gösterir ve bu işlemleri gerçekleştirebilirsiniz.

navigator.mediaSession.setActionHandler('previoustrack', function () {
  // User clicked "Previous Track" button.
});

navigator.mediaSession.setActionHandler('nexttrack', function () {
  // User clicked "Next Track" button.
});

Bunu uygulamalı olarak görmek için resmi Medya Oturumu örneğini deneyin.

Pencere içinde pencere boyutunu alma

Video Pencere İçinde Pencere'ye girip çıktığında video kalitesini ayarlamak istiyorsanız Pencere İçinde Pencere pencere boyutunu bilmeniz ve kullanıcı pencereyi manuel olarak yeniden boyutlandırdığında bildirim almanız gerekir.

Aşağıdaki örnekte, oluşturulduğunda veya yeniden boyutlandırıldığında Pencere İçinde Pencere penceresinin genişliğinin ve yüksekliğinin nasıl alınacağı gösterilmektedir.

let pipWindow;

videoElement.addEventListener('enterpictureinpicture', function (event) {
  pipWindow = event.pictureInPictureWindow;
  console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
  pipWindow.addEventListener('resize', onPipWindowResize);
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  pipWindow.removeEventListener('resize', onPipWindowResize);
});

function onPipWindowResize(event) {
  console.log(
    `> Window size changed to ${pipWindow.width}x${pipWindow.height}`
  );
  // TODO: Change video quality based on Picture-in-Picture window size.
}

Pencere içinde pencere boyutunda yapılan her küçük değişiklik, her yeniden boyutlandırmada pahalı bir işlem yapıyorsanız performans sorunlarına neden olabilecek ayrı bir etkinliği tetikleyeceğinden, yeniden boyutlandırma etkinliğine doğrudan bağlanmamanızı öneririz. Başka bir deyişle, yeniden boyutlandırma işlemi etkinlikleri tekrar tekrar çok hızlı bir şekilde tetikler. Bu sorunu çözmek için kısıtlama ve geri döndürme gibi yaygın teknikleri kullanmanızı öneririm.

Özellik desteği

Pencere İçinde Pencere Web API'si desteklenmeyebilir. Bu nedenle, aşamalı geliştirme sunmak için bunu tespit etmeniz gerekir. Desteklendiğinde bile kullanıcı tarafından kapatılabilir veya bir izin politikası tarafından devre dışı bırakılabilir. Neyse ki bunu belirlemek için yeni document.pictureInPictureEnabled boole'sini kullanabilirsiniz.

if (!('pictureInPictureEnabled' in document)) {
  console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
  console.log('The Picture-in-Picture Web API is disabled.');
}

Bir videonun belirli bir düğme öğesine uygulandığında, Pencere İçinde Pencere düğmenizin görünürlüğünü bu şekilde ayarlayabilirsiniz.

if ('pictureInPictureEnabled' in document) {
  // Set button ability depending on whether Picture-in-Picture can be used.
  setPipButton();
  videoElement.addEventListener('loadedmetadata', setPipButton);
  videoElement.addEventListener('emptied', setPipButton);
} else {
  // Hide button if Picture-in-Picture is not supported.
  pipButtonElement.hidden = true;
}

function setPipButton() {
  pipButtonElement.disabled =
    videoElement.readyState === 0 ||
    !document.pictureInPictureEnabled ||
    videoElement.disablePictureInPicture;
}

MediaStream video desteği

MediaStream nesnelerini (ör. getUserMedia(), getDisplayMedia(), canvas.captureStream()) oynatan videolar da Chrome 71'de Pencere İçinde Pencere özelliğini destekler. Yani, kullanıcının web kamerası video akışını, görüntülü video akışını veya hatta bir tuval öğesini içeren bir Pencere İçinde Pencere penceresi gösterebilirsiniz. Aşağıda gösterildiği gibi, Pencere İçinde Pencere moduna girmek için video öğesinin DOM'ye eklenmesine gerek olmadığını unutmayın.

Kullanıcının web kamerasını Pencere İçinde Pencere penceresinde göster

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Ekranı Pencere İçinde Pencere penceresinde göster

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Tuval öğesini Pencere İçinde Pencere penceresinde göster

const canvas = document.createElement('canvas');
// Draw something to canvas.
canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);

const video = document.createElement('video');
video.muted = true;
video.srcObject = canvas.captureStream();
video.play();

// Later on, video.requestPictureInPicture();

canvas.captureStream() ile Media Session API'yi birleştirerek Chrome 74'te bir ses oynatma listesi penceresi oluşturabilirsiniz. Resmi müzik şarkı listesi örneğini inceleyin.

Pencere İçinde Pencere penceresinde müzik listesi
Şekil 2. Pencere içinde pencere modunda sesli oynatma listesi

Örnekler, demolar ve codelab'ler

Pencere İçinde Pencere Web API'sini denemek için resmi Pencere İçinde Pencere örneğimize göz atın.

Ardından demolar ve codelab'ler sunulacak.

Sonraki adımlar

Öncelikle, şu anda API'nin hangi bölümlerinin Chrome ve diğer tarayıcılarda uygulandığını öğrenmek için uygulama durumu sayfasına göz atın.

Yakın gelecekte aşağıdaki değişiklikleri görebilirsiniz:

Tarayıcı desteği

Pencere İçinde Pencere Web API'si Chrome, Edge, Opera ve Safari'de desteklenir. Ayrıntılar için MDN konusuna bakın.

Kaynaklar

Pencere İçinde Pencere özelliğindeki çalışmaları ve bu makaledeki yardımları için Mounir Lamouri ve Jennifer Apacible'a çok teşekkür ederiz. Standartlaştırma çalışmalarına katılan herkese çok teşekkür ederiz.