- {% trans %}index.tag{% endtrans %}
- {% for tag in video.tags %}
-
- {% endfor %}
- {% trans %} index.created_by{% endtrans %} :
@@ -43,7 +84,7 @@
{% set videoLink = absolute_url(path('video_download',{'id':video.id})) %}
{% trans from "video" %}share.download{% endtrans %} :
{{ videoLink }}
- {% set videoLinkHls = absolute_url(path('video_hls',{'id':video.id})) %}
+ {% set videoLinkHls = absolute_url(path('stream_video',{'id':video.id})) %}
{% trans from "video" %}share.hls{% endtrans %} :
{{ videoLinkHls }}
{% set videoLinkLti = absolute_url(path('lti_show',{'id':video.id})) %}
diff --git a/app/config/security.yml b/app/config/security.yml
index 791dd39..6f7d8f8 100644
--- a/app/config/security.yml
+++ b/app/config/security.yml
@@ -34,4 +34,4 @@ security:
- { path: ^/video/, role: IS_AUTHENTICATED_FULLY }
- { path: ^/stream/, role: IS_AUTHENTICATED_FULLY }
- { path: ^/home, role: IS_AUTHENTICATED_FULLY }
- - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/, role: IS_AUTHENTICATED_FULLY }
diff --git a/bin/encoder/conf.sh b/bin/encoder/conf.sh
index 37d70b8..b61df98 100644
--- a/bin/encoder/conf.sh
+++ b/bin/encoder/conf.sh
@@ -2,6 +2,8 @@
##################################################################
#configuration
##################################################################
-MONITORDIR="web/e-media-data/video"
+MONITORDIR="..//e-media-data/video"
LOGFILE='var/logs/encoder.log'
-declare -a VIDEO_EXTENSION=("mp4" "flv" "mkv");
\ No newline at end of file
+SEGMENT_DURATION=1
+declare -a VIDEO_EXTENSION=("mp4" "flv" "mkv");
+declare -a FRAME_SIZES_TO_PROCESS=("1280x720" "720x480");
\ No newline at end of file
diff --git a/bin/encoder/encodeAll.sh b/bin/encoder/encodeAll.sh
index 8d0937b..c30e7de 100755
--- a/bin/encoder/encodeAll.sh
+++ b/bin/encoder/encodeAll.sh
@@ -23,13 +23,16 @@ done
for CURRENTDIR in $(ls -d ${MONITORDIR}/*)
do
echo ${CURRENTDIR}
- if [ -f ${CURRENTDIR}/index.m3u8 ]; then
- if [ "${ENCODE_EXISTING}" = true ]; then
- launchEncoding ${CURRENTDIR}
+ for FRAME_SIZE in "${FRAME_SIZES_TO_PROCESS[@]}"
+ do
+ if [ -f ${CURRENTDIR}/${FRAME_SIZE}/index.m3u8 ]; then
+ if [ "${ENCODE_EXISTING}" = true ]; then
+ launchEncoding ${CURRENTDIR}
+ else
+ log "$LEVEL_INFO" "${CURRENTDIR}/${FRAME_SIZE} already encoded!"
+ fi
else
- log "$LEVEL_INFO" "${CURRENTDIR} already encoded!"
+ launchEncoding ${CURRENTDIR}
fi
- else
- launchEncoding ${CURRENTDIR}
- fi
+ done
done
\ No newline at end of file
diff --git a/bin/encoder/encoder.sh b/bin/encoder/encoder.sh
index ba7133e..db5da6e 100755
--- a/bin/encoder/encoder.sh
+++ b/bin/encoder/encoder.sh
@@ -1,13 +1,29 @@
#!/bin/bash
#this scripts to encode video: from a video file to a .index.m3u8 file for Http Live Streaming!
+# https://opensource.com/article/17/6/ffmpeg-convert-media-file-formats
source 'bin/encoder/lib.sh'
NEWFILE=$1
log "$LEVEL_INFO" "video to encode detected: ${NEWFILE}"
DIR=$(dirname ${NEWFILE})
+FILENAME=$(basename "$NEWFILE")
+EXTENSION="${FILENAME##*.}"
+FILENAME="${FILENAME%.*}"
+echo "evaluating ${file} : filename is ${FILENAME} and extension is ${EXTENSION}"
+
rm -f ${DIR}/*.m3u8 ${DIR}/*.ts ${DIR}/lock ${DIR}/error
touch ${DIR}/lock
log "$LEVEL_INFO" "encoding video: ${NEWFILE}..."
-ffmpeg -i ${NEWFILE} -hls_list_size 0 -f hls ${DIR}/index.m3u8 2> /dev/null
+
+for FRAME_SIZE in "${FRAME_SIZES_TO_PROCESS[@]}"
+do
+ rm -rf ${DIR}/${FRAME_SIZE}
+ mkdir -p ${DIR}/${FRAME_SIZE}
+ #generate framesize
+ ffmpeg -i ${NEWFILE} -c:a copy -s ${FRAME_SIZE} "${DIR}/${FRAME_SIZE}/${FILENAME}.${EXTENSION}" 2> /dev/null
+ ffmpeg -i ${DIR}/${FRAME_SIZE}/${FILENAME}.${EXTENSION} -hls_time ${SEGMENT_DURATION} -g ${SEGMENT_DURATION} -hls_list_size 0 -f hls ${DIR}/${FRAME_SIZE}/index.m3u8 2> /dev/null
+done
+#generate hls segment and playlist for original uploaded file too
+#ffmpeg -i ${NEWFILE} -hls_list_size 0 -f hls ${DIR}/index.m3u8 2> /dev/null
if [ $? -eq 0 ]; then
log "$LEVEL_INFO" "video encoded: ${NEWFILE}"
else
diff --git a/package.json b/package.json
index 269a4d8..b97401f 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"load-grunt-tasks": "^0.6.0",
"slabText": "freqdec/slabText#^2.2.0",
"videojs-contrib-hls": "^5.12.2",
+ "videojs-qualityselector": "0.0.4",
"videosjs-assets": "benIT/videosjs-assets"
},
"scripts": {
diff --git a/src/AppBundle/Controller/StreamController.php b/src/AppBundle/Controller/StreamController.php
index f132308..5f2d706 100644
--- a/src/AppBundle/Controller/StreamController.php
+++ b/src/AppBundle/Controller/StreamController.php
@@ -3,11 +3,7 @@
namespace AppBundle\Controller;
use AppBundle\Entity\Video;
-use AppBundle\Exception\VideoEncodingErrorException;
-use AppBundle\Exception\VideoEncodingPendingException;
-use AppBundle\Exception\VideoNotEncodedException;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -17,92 +13,58 @@ class StreamController extends Controller
{
use ControllerUtilsTrait;
- /**
- * @see https://symfony.com/doc/current/components/http_foundation.html#serving-files
- * @see apache mod_xsendfile: https://tn123.org/mod_xsendfile/
- * @see nginx X-accel: https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
- * @param Request $request
- * @param Video $video
- * @return BinaryFileResponse
- * @throws \Exception
- */
- public function downloadAction(Request $request, Video $video)
- {
- $videoPath = $this->get('vich_uploader.storage')->resolvePath($video, 'videoFile');
- $response = new BinaryFileResponse($videoPath);
- if (filter_var(getenv('USE_X_SENDFILE_MODE'), FILTER_VALIDATE_BOOLEAN)) {
- BinaryFileResponse::trustXSendfileTypeHeader();
- $serverSoftware = $request->server->get('SERVER_SOFTWARE');
- //determine header according to server software to serve file faster directly by server instead of using php
- if (preg_match('/nginx/', $serverSoftware)) {
- if (!$nginxLocationXSendFile = getenv('NGINX_LOCATION_X_SEND_FILE')) {
- throw new ParameterNotFoundException('nginx_location_x_send_file');
- }
- // slash management in lginx stream location
- $nginxLocationXSendFile = substr($nginxLocationXSendFile, -1) === '/' ? $nginxLocationXSendFile : $nginxLocationXSendFile . '/';
- $nginxLocationXSendFile = substr($nginxLocationXSendFile, 0, 1) === '/' ? $nginxLocationXSendFile : '/' . $nginxLocationXSendFile;
- $response->headers->set('X-Accel-Redirect', $nginxLocationXSendFile . 'video/' . basename(pathinfo($videoPath)['dirname']) . '/' . pathinfo($videoPath)['basename']);
- } elseif (preg_match('/apache/', $serverSoftware)) {
- $response->headers->set('X-Sendfile', $videoPath);
- } else {
- throw new \Exception(sprintf('server "%s" not supported', $serverSoftware));
- }
- $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, basename($videoPath));
- }
- return $response;
- }
-
-
/**
* secure the HLS m3u8 and segment download
* @param Request $request
* @param Video $video
* @param $file
+ * @param $frameSize
* @return BinaryFileResponse
*/
- public function downloadHlsPlaylistFileAction(Request $request, Video $video, $file)
+ public function downloadHlsFileAction(Request $request, Video $video, $file, $frameSize)
{
$videoPath = $this->get('vich_uploader.storage')->resolvePath($video, 'videoFile');
- $filePath = pathinfo($videoPath)['dirname'] . '/' . $file;
+ $filePath = $frameSize ? pathinfo($videoPath)['dirname'] . '/' . $frameSize . '/' . $file : pathinfo($videoPath)['dirname'] . '/' . $file;
$response = new BinaryFileResponse($filePath);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, basename($filePath));
return $response;
}
+
/**
* Http Live Streaming
* @see https://en.wikipedia.org/wiki/HTTP_Live_Streaming
* @param Request $request
* @param Video $video
+ * @param $frameSize
* @return \Symfony\Component\HttpFoundation\Response
*/
- public function HlsAction(Request $request, Video $video)
+ public function streamAction(Request $request, Video $video, $frameSize)
{
-
$videoPath = $this->get('vich_uploader.storage')->resolvePath($video, 'videoFile');
$fs = new Filesystem();
$playlistFile = getenv('HLS_PLAYLIST_NAME');
- $playlistFileLocation = pathinfo($videoPath)['dirname'] . '/' . $playlistFile;
- $errorFileLocation = pathinfo($videoPath)['dirname'] . '/' . 'error';
- $lockFileLocation = pathinfo($videoPath)['dirname'] . '/' . 'lock';
+ $playlistFileLocation = join('/', [pathinfo($videoPath)['dirname'], $frameSize, $playlistFile]);
+ $errorFileLocation = join('/', [pathinfo($videoPath)['dirname'], $frameSize, 'error']);
+ $lockFileLocation = join('/', [pathinfo($videoPath)['dirname'], $frameSize, 'lock']);
$error = false;
if ($fs->exists($errorFileLocation)) {
$error = $this->get('translator')->trans('encoding.error', [], 'stream');
$this->flashMessage(ControllerUtilsTrait::$flashDanger, $error);
- }elseif ($fs->exists($lockFileLocation)) {
+ } elseif ($fs->exists($lockFileLocation)) {
$error = $this->get('translator')->trans('encoding.pending', [], 'stream');
$this->flashMessage(ControllerUtilsTrait::$flashInfo, $error);
- }elseif(!$fs->exists($playlistFileLocation)) {
+ } elseif (!$fs->exists($playlistFileLocation)) {
$error = $this->get('translator')->trans('encoding.no-playlist', [], 'stream');
$this->flashMessage(ControllerUtilsTrait::$flashDanger, $error);
}
- return $this->render('stream/hls.html.twig', array(
+ return $this->render('stream/stream.html.twig', array(
'error' => $error,
'video' => $video,
- 'playlistFileLocation' => $playlistFileLocation,
+ 'frameSize' => $frameSize
));
}
}
\ No newline at end of file
diff --git a/src/AppBundle/Controller/VideoController.php b/src/AppBundle/Controller/VideoController.php
index 2d64f23..eddebb9 100644
--- a/src/AppBundle/Controller/VideoController.php
+++ b/src/AppBundle/Controller/VideoController.php
@@ -3,8 +3,13 @@
namespace AppBundle\Controller;
use AppBundle\Entity\Video;
+use AppBundle\Services\XsendFileResponseManager;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+
/**
* Video controller.
@@ -164,4 +169,39 @@ private function createDeleteForm(Video $video)
->setAction($this->generateUrl('video_delete', array('id' => $video->getId())))
->getForm();
}
+
+ /**
+ * @see https://symfony.com/doc/current/components/http_foundation.html#serving-files
+ * @see apache mod_xsendfile: https://tn123.org/mod_xsendfile/
+ * @see nginx X-accel: https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
+ * @param Request $request
+ * @param Video $video
+ * @return BinaryFileResponse
+ * @throws \Exception
+ */
+ public function downloadAction(Request $request, Video $video)
+ {
+ $videoPath = $this->get('vich_uploader.storage')->resolvePath($video, 'videoFile');
+ $response = new BinaryFileResponse($videoPath);
+ if (filter_var(getenv('USE_X_SENDFILE_MODE'), FILTER_VALIDATE_BOOLEAN)) {
+ BinaryFileResponse::trustXSendfileTypeHeader();
+ $serverSoftware = $request->server->get('SERVER_SOFTWARE');
+ //determine header according to server software to serve file faster directly by server instead of using php
+ if (preg_match('/nginx/', $serverSoftware)) {
+ if (!$nginxLocationXSendFile = getenv('NGINX_LOCATION_X_SEND_FILE')) {
+ throw new ParameterNotFoundException('nginx_location_x_send_file');
+ }
+ // slash management in lginx stream location
+ $nginxLocationXSendFile = substr($nginxLocationXSendFile, -1) === '/' ? $nginxLocationXSendFile : $nginxLocationXSendFile . '/';
+ $nginxLocationXSendFile = substr($nginxLocationXSendFile, 0, 1) === '/' ? $nginxLocationXSendFile : '/' . $nginxLocationXSendFile;
+ $response->headers->set('X-Accel-Redirect', $nginxLocationXSendFile . '/' . basename(pathinfo($videoPath)['dirname']) . '/' . pathinfo($videoPath)['basename']);
+ } elseif (preg_match('/apache/', $serverSoftware)) {
+ $response->headers->set('X-Sendfile', $videoPath);
+ } else {
+ throw new \Exception(sprintf('server "%s" not supported', $serverSoftware));
+ }
+ $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, basename($videoPath));
+ }
+ return $response;
+ }
}
diff --git a/src/AppBundle/Resources/config/routing/stream.yml b/src/AppBundle/Resources/config/routing/stream.yml
index 19fd868..35ed705 100644
--- a/src/AppBundle/Resources/config/routing/stream.yml
+++ b/src/AppBundle/Resources/config/routing/stream.yml
@@ -1,13 +1,8 @@
-video_download:
- path: /{id}
- defaults: { _controller: "AppBundle:Stream:download" }
+stream_video:
+ path: /{id}/{frameSize}
+ defaults: { _controller: "AppBundle:Stream:stream", frameSize: '720x480' }
methods: GET
-video_hls:
- path: /{id}/hls/{file}
- defaults: { _controller: "AppBundle:Stream:Hls", file: null }
- methods: GET
-video_hls_download_playlist_file:
- path: /{id}/hls-download/{file}
- defaults: { _controller: "AppBundle:Stream:downloadHlsPlaylistFile" }
- methods: GET
-
+stream_download_hls_file:
+ path: /{id}/hls/framesize/{frameSize}/file/{file}
+ defaults: { _controller: "AppBundle:Stream:downloadHlsFile" }
+ methods: GET
\ No newline at end of file
diff --git a/src/AppBundle/Resources/config/routing/video.yml b/src/AppBundle/Resources/config/routing/video.yml
index b39016b..475f469 100644
--- a/src/AppBundle/Resources/config/routing/video.yml
+++ b/src/AppBundle/Resources/config/routing/video.yml
@@ -31,4 +31,9 @@ video_delete:
video_search:
path: /search
defaults: { _controller: "AppBundle:Video:search" }
+ methods: GET
+
+video_download:
+ path: /download/{id}
+ defaults: { _controller: "AppBundle:Video:download" }
methods: GET
\ No newline at end of file
diff --git a/web/assets/js/app.js b/web/assets/js/app.js
index e68908a..01c99ef 100644
--- a/web/assets/js/app.js
+++ b/web/assets/js/app.js
@@ -7,6 +7,7 @@
$('select').chosen({
allow_single_deselect: true,
placeholder_text: Translator.trans('select.multiple', {}, 'common'),
+ width: '100%'
});
$('#loading').fadeOut();
});