Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if YouTube video is widescreen?

I would like to know with certainty if a YouTube video is widescreen or not using the v3 API. There are many old videos that have a 4:3 ratio, so I need to detect this.

This was possible with API v2, but it is officially retired now. Here are the API v3 docs.

An API call looks something like this:

https://www.googleapis.com/youtube/v3/videos?id=[VIDEOID]&part=snippet&key=[DEVELOPERKEY]

Also, the thumbnail data always returns dimensions of 4:3, so that doesn't help. Here is an example:

[thumbnails] => Array
(
    [default] => Array
    (
        [url] => https://i.ytimg.com/vi/nnnnnnnnn/default.jpg
        [width] => 120
        [height] => 90
    )
    ...
)

Any ideas?

(I'm currently hacking this by analyzing pixels in the thumbnails where tell-tale black bars on 4:3 videos will be.)

Here is a sample video in 4:3 ratio:

https://www.youtube.com/watch?v=zMJ-Dl4eJu8 (old martial arts video)

martial arts in 4:3

and one in 16:9:

https://www.youtube.com/watch?v=7O2Jqi-LhEI (a new workout video)

workout video


Update: One promising suggestion was to explore fileDetails.videoStreams[].aspectRatio but it seems that this is only available to the video owner. Otherwise requesting fileDetails results in

The request cannot access user rating information. This error may occur because the request is not properly authorized

like image 723
Drakes Avatar asked Jul 02 '15 15:07

Drakes


2 Answers

If you're open to using a different method other than V3 of the API, then I believe it is possible via the oEmbed API.

http://www.youtube.com/oembed?url={VIDEO_URL}&format=json

Like so:

http://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=zMJ-Dl4eJu8&format=json

Would produce:

{  
    "provider_url":"https:\/\/www.youtube.com\/",
    "thumbnail_url":"https:\/\/i.ytimg.com\/vi\/zMJ-Dl4eJu8\/hqdefault.jpg",
    "thumbnail_height":360,
    "height":344,
    "type":"video",
    "version":"1.0",
    "html":"\u003ciframe width=\"459\" height=\"344\" src=\"https:\/\/www.youtube.com\/embed\/zMJ-Dl4eJu8?feature=oembed\" frameborder=\"0\" allowfullscreen\u003e\u003c\/iframe\u003e",
    "author_name":"hadronica2",
    "width":459,
    "provider_name":"YouTube",
    "author_url":"https:\/\/www.youtube.com\/user\/hadronica2",
    "title":"Aikido - Kazuo Chiba sensei - 1\u00ba part",
    "thumbnail_width":480
}

In the examples you've given, the output was as follows:

http://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=zMJ-Dl4eJu8&format=json

Width: 459
Height: 344
Ratio: w/h = 1.3343 = 4:3 (ish)

http://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=zMJ-Dl4eJu8&format=json

Width: 480
Height: 270
Ratio: w/h = 1.7777 = 16/9

This appears to work in the examples you've provided.

like image 96
Jamie Bicknell Avatar answered Oct 20 '22 14:10

Jamie Bicknell


Here is the abridged version that I have been using since v2 of the API retired.

It tests a few points on the top and bottom of the default.jpg thumbnail image of a given video where black bars might be. A vertically opposite point from a top point is tested to see if those pixels are similar to each other to within some delta. This is repeated for a few more points.

function isWidescreen($video = null) {

    // LOGIC:
    // 4:3 videos will have default.jpg with no top black bars
    // 16:9 videos will have black top and bottom borders on default.jpg

    // Get the default thumbnail (may have black bars on top and bottom)
    $response = self::accessCurlObj()->get("https://i.ytimg.com/vi/{$video}/default.jpg");
    $defaultImgRes = imagecreatefromstring($response);

    $samplePoints = array(array(20,2), array(40,4), array(60,6), array(80,8));

    // Scan a few points for equality between top and bottom
    $height = imagesy($defaultImgRes);
    foreach($samplePoints as $point) {
        // Top
        $rgbTop = imagecolorat($defaultImgRes, $point[0], $point[1]);
        $colorsTop = imagecolorsforindex($defaultImgRes, $rgbTop);

        // Bottom
        $rgbBottom = imagecolorat($defaultImgRes, $point[0], $height - $point[1]);
        $colorsBottom = imagecolorsforindex($defaultImgRes, $rgbBottom);

        // If these arrays are not close, then let's call this 4:3 aspect
        if(!$this->areArraysClose($colorsTop, $colorsBottom, 20)) {
            return false;
        }
    }

    // Default to widescreen
    return true;
}

// Determine if the numeric values in the RGBA array are within some delta from each other
function areArraysClose(&$a, &$b, $delta = 10) {
    foreach($a as $key => $val) {
        if(abs($val - $b[$key]) > $delta) {
            return false;
        }
    }
    return true;
}

This seems to be working sufficiently enough. An obvious improvement is to check if the pixels are close to black, or apply some image processing to remove black bars automatically then check the dimensions of the remaining image.

However, my hope was that a domain-knowledgeable SO member would have a better solution before going deeper down this rabbit hole... and someone came through.

like image 1
Drakes Avatar answered Oct 20 '22 15:10

Drakes