PicVid

A simple PHP script that hides video data within image files.

Demo

Using a hook based on Hls loader:

Using EXT-X-BYTERANGE option in m3u8 file:

AliExpress image hosting has adopted a strategy of converting images to the webp format, so the previous demo based on AliExpress image hosting is no longer functional. The demo has now been migrated to Gitee Pages for learning and reference purposes only. (Video loading may be slightly slow).

Thanks to

Requirements

Config

Edit pv.php:

$output_dir = 'output';  /*output directory(based on the location of pv.php)*/  
$if_windows = true;  /*whether you are using Windows*/  
$clearLogOnStart = true;  /*clear executeLog.log when running the script*/  
$maxTSFileSize = 5242880; /*(In bytes) Maximum size of each split out ts file (more than that will be compressed) */  
$mergeTSUpTo = 2097152; /*(In bytes,cannot be set over $maxTSFileSize) The max ts file size generated when merging several small ts files*/  
$disguisePic = __DIR__ . '/small.png'; /*The picture used for disguising, we suggest using a jpg or png file*/  
$exitBigTSNum = 0.50; /*(×100%)When large(>$maxTSFileSize) ts files' total num account for more than $exitBigTSNum, the script will exit and recommend you to use -recomp*/
$useByteRange = true; /*Will you use the BYTERANGE option in the generated m3u8 file, you can also use '--nobr' to disable it in the command line*/

Edit uploadAPI.php:

/*PV.PHP UPLOAD PIC, the first return value:<pic url> or <bool:false> in stand of error*/
function PVUpload($path){
	//pic upload function
	$rawdata=
	$imgurl=
	return [(empty($imgurl) ? false : $imgurl),$rawdata];
}   

You should write your own function that uploads pics to the picbed through the picbed’s API.

for example , if you make a function upload($path) to upload each picture , and it returns with the JSON below:

{"code":0,"data":{"size":"264","url":"https://s1.ax1x.com/2020/09/16/wgnGxf.png"}}  

then:

/*PV.PHP UPLOAD PIC, the first return value:<pic url> or <bool:false> in stand of error*/
function PVUpload($path){
	//pic upload function
	$rawdata=upload($path);
	$parsed_data=json_decode($rawdata,true);
	$imgurl=$parsed_data['data']['url'];
	return [(empty($imgurl) ? false : $imgurl),$rawdata];
}   

Bypass uploading

By default, pv.php will upload the disguised pics to picbeds through uploadAPI.php.

If you want to bypass the upload process, and just save these pics to local, you can use pv_bypass_upload.php and saveToLocal.php instead.

In this way, your directory structure will be as follows:

YourDir
├── executeLog.log # ffmpeg output logs
├── output # output directory
│   ├── 0.ts # ts files
│   ├── 1.ts
│   ├── pics # disguised pictures you saved
│   │   ├── temppic1440-0.jpg
│   │   └── temppic999-1.jpg
│   ├── video.m3u8.jpg # m3u8 file disguised in jpg
│   └── video.real.m3u8 # m3u8 file without disguise (recommend)
├── potato.jpg # disguising picture
├── pv_bypass_upload.php 
├── rick.mp4 # original video
└── saveToLocal.php # required by pv_bypass_upload.php

You may want to upload those pics to picbeds manually, and the URLs in m3u8 file need to be updated.

Fortunately, you will be asked to type in the URL of the parent directory of the place you would like to upload the disguised pictures.

The prompt will look like this:

Example: If you are to upload pic.png to https://example.com/abc/pic.png
You should type in 'https://example.com/abc/'
Please input the parent URL of the pics to be uploaded: https://test/

Preview: Pic test.png corresponds to https://test/test.png
Type in 'yes' to confirm: yes
OK.

In the example above, I use https://test/ as the parent URL, and the generated m3u8 file will look like this:

...
#EXTINF:10.08,
#EXT-X-BYTERANGE:3408252@3610
https://test/temppic941-0.jpg
...

If I leave the input blank, the generated m3u8 file will be as follows:

...
#EXTINF:10.08,
#EXT-X-BYTERANGE:3408252@3610
temppic941-0.jpg
...

Usage

Type at the command line: php <pv.php|pv_bypass_upload.php> [--nobr] [--recomp] -v videofile

Notice

How to play

On the other hand

⚠️ Currently not working without EXT-X-BYTERANGE, you need to set $useByteRange = true; in the config so as to play in this way.


Just put aside video.m3u8.png and take a look at video.real.m3u8, you just need to delete the hook code of Hls and add codes to make it alternative for natively m3u8 support browser(Such as Safari):

 var video = document.getElementById('video');
  var videoSrc = 'video.real.m3u8';
  var config = {
	  debug:true
  };
 if (Hls.isSupported()) {
    var hls = new Hls(config);
    hls.loadSource(videoSrc);
    hls.attachMedia(video);
  }else{
    video.src = videoSrc;
    alert('你的浏览器自带支持m3u8,如果无法播放请发issue\nYour browser natively support m3u8 playing, create issue if it doesn\'t work.');  
  }  

In this way, your video is available in almost all of the main stream web browser.

Additional notice

Special thanks


MIT License.