
Recherche avancée
Autres articles (86)
-
Websites made with MediaSPIP
2 mai 2011, parThis page lists some websites based on MediaSPIP.
-
Creating farms of unique websites
13 avril 2011, parMediaSPIP platforms can be installed as a farm, with a single "core" hosted on a dedicated server and used by multiple websites.
This allows (among other things) : implementation costs to be shared between several different projects / individuals rapid deployment of multiple unique sites creation of groups of like-minded sites, making it possible to browse media in a more controlled and selective environment than the major "open" (...) -
XMP PHP
13 mai 2011, parDixit Wikipedia, XMP signifie :
Extensible Metadata Platform ou XMP est un format de métadonnées basé sur XML utilisé dans les applications PDF, de photographie et de graphisme. Il a été lancé par Adobe Systems en avril 2001 en étant intégré à la version 5.0 d’Adobe Acrobat.
Étant basé sur XML, il gère un ensemble de tags dynamiques pour l’utilisation dans le cadre du Web sémantique.
XMP permet d’enregistrer sous forme d’un document XML des informations relatives à un fichier : titre, auteur, historique (...)
Sur d’autres sites (5218)
-
Unable to mux two live streams from webcam using ffmpeg in linux
24 octobre 2013, par user2902884We are trying to multiplex two live streams from webcams into an output file using ffmpeg in linux as follows,
fmpeg -i "http://10.41.2.57:8090/webcam.asf" -i "http://10.41.2.49:8090/webcam.asf" -map 0:0 -map 1:0 -t 60 "/home/../MuxLiveStream1.flv"
ffmpeg stops responding at after a while and the console shows the following output,
FFmpeg version SVN-r0.5.9-4:0.5.9-0ubuntu0.10.04.3, Copyright (c) 2000-2009 Fabrice Bellard, et al.
configuration: --extra-version=4:0.5.9-0ubuntu0.10.04.3 --prefix=/usr --enable-avfilter --enable-avfilter-lavf --enable-vdpau --enable-bzlib --enable-libgsm --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvorbis --enable-pthreads --enable-zlib --disable-stripping --disable-vhook --enable-runtime-cpudetect --enable-gpl --enable-postproc --enable-swscale --enable-x11grab --enable-libdc1394 --enable-shared --disable-static
libavutil 49.15. 0 / 49.15. 0
libavcodec 52.20. 1 / 52.20. 1
libavformat 52.31. 0 / 52.31. 0
libavdevice 52. 1. 0 / 52. 1. 0
libavfilter 0. 4. 0 / 0. 4. 0
libswscale 0. 7. 1 / 0. 7. 1
libpostproc 51. 2. 0 / 51. 2. 0
built on Jan 24 2013 19:42:59, gcc: 4.4.3
Seems stream 0 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 2.00 (2/1)
Input #0, flv, from 'http:10.41.2.154:8090/webcam.flv':
Duration: 00:00:00.00, start: 1587016.579000, bitrate: 200 kb/s
Stream #0.0: Video: flv, yuv420p, 320x240, 200 kb/s, 2 tbr, 1k tbn, 1k tbc
Seems stream 0 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 2.00 (2/1)
Input #1, flv, from 'http:10.41.2.57:8090/webcam.flv':
Duration: 00:00:00.00, start: 1587230.806000, bitrate: 200 kb/s
Stream #1.0: Video: flv, yuv420p, 320x240, 200 kb/s, 2 tbr, 1k tbn, 1k tbc
Number of stream maps must match number of output streamsIs there a mistake in the command or is there anything else that needs to be done ?
-
where to put the watermark png file in this code ?
15 août 2017, par asifthis is a line from ffmpeg video converter php script ;
$ffmpeg." -i ".$video_to_convert." -vcodec flv -f flv -r 30 -b ".$quality."-ab 128000 -ar".$audio." -s ".$size." ".$converted_vids.$name.".".$new_format."
where :
.$video_to_convert. = input file
.$converted_vids.$name. = output file
.$new_format. ; = file extention format
now i want to add watermark.png file, if i put any where i got error conversion
as i added the linelogo.png -filter_complex 'overlay
in video quality line, i got errorError ;
ffmpeg version N-71954-gbc6f84f Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.4.7 (GCC) 20120313 (Red Hat 4.4.7-16)
configuration: --prefix=/usr --enable-version3 --enable-gpl --enable-shared --enable-libmp3lame --enable-libvorbis --enable-libtheora --enable-libvpx --enable-libx264 --enable-libxvid --enable-libopencore-amrwb --enable-libopencore-amrnb --enable-postproc --enable-nonfree --enable-pthreads --enable-x11grab --enable-libfaac --enable-libopenjpeg --enable-zlib --disable-doc
libavutil 54. 23.101 / 54. 23.101
libavcodec 56. 37.101 / 56. 37.101
libavformat 56. 31.102 / 56. 31.102
libavdevice 56. 4.100 / 56. 4.100
libavfilter 5. 16.101 / 5. 16.101
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 1.100 / 1. 1.100
libpostproc 53. 3.100 / 53. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'uploaded/1502801111_21.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf56.31.102
Duration: 00:02:27.32, start: 0.023220, bitrate: 635 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 480x320 [SAR 1:1 DAR 3:2], 476 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 151 kb/s (default)
Metadata:
handler_name : SoundHandler
Please use -b:a or -b:v, -b is ambiguous
Cannot find a matching stream for unlabeled input pad 1 on filter Parsed_overlay_0This is index.php file scipt of conversion :
v<?php include("templates/header.php");?>
<div>
<?php
require_once('languages/en.php');
include ("includes/settings.php");
include("includes/functions.php");
include("includes/LIB_parse.php");
include("classes/other.php");
cron(); // to run cron job to delete all files
if(isset($_POST['uploader_0_name']) or isset ($_GET['uploaded_name']))
{
if(isset($_POST['uploader_0_name']))
{
list($_SESSION['original_name'], $ext) = explode('.',$_POST['uploader_0_name']);
}else{
$fullname=$_GET['uploaded_name'];
}
if(isset($_POST['uploader_0_name']))
{
list($_SESSION['original_name'], $ext) = explode('.',$_POST['uploader_0_name']);
}else{
list($_SESSION['original_name'], $ext) = explode('.',$_GET['uploaded_name']);
$_SESSION['original_name']= return_between($_GET['uploaded_name'], '_', "$ext", 'EXCL') ;
$_SESSION['original_name']= str_replace(".","",$_SESSION['original_name']);
}
if(isset($_POST['uploader_0_name']))
{
$fullname= $_POST['uploader_0_tmpname'];
}else{
$fullname=$_GET['uploaded_name'];
}
$ext= pathinfo($fullname, PATHINFO_EXTENSION);
$_SESSION['name']=RemoveExtension($fullname);
$store_dir="uploaded/";
$_SESSION['converted_vids']=$converted_vids;
$_SESSION['temp_dir']=$store_dir;
// if those directotires don't exist - create them and give them read/write permissions
if(!is_dir($store_dir)) mkdir($store_dir, 0777);
if(!is_dir($converted_vids)) mkdir($converted_vids, 0777);
$extension = substr($fullname, strrpos($fullname, "."));
$extension = strtolower($extension);
$video_to_convert=$store_dir.$_SESSION['name'];
$_SESSION['video_to_convert']=$video_to_convert;
$_SESSION['extension']=$extension;
/*******************************
check extenxions
***************************************************/
if (!in_array($ext, $allowedExtensions))
{
echo '<div style="text-align:center"><strong>'.$ext."</strong><br /> is an invalid file type!<br />Supported files are:<br /><strong>";
foreach ($allowedExtensions as $value) { echo $value . "<br />"; }
echo "</strong><br />";
echo "<a href="http://stackoverflow.com/feeds/tag/\"index.php\"">"."<img src="http://stackoverflow.com/feeds/tag/\"images/red-back.png\"" border="\"0\"" style='max-width: 300px; max-height: 300px' /></a></div>";
include("templates/footer.php");
exit;
}
?>
<center>
<form action="index.php" method="post" target="_self">
<br />
<fieldset>
<legend class="show_legend">Convert To</legend>
<div class="radio_buttons">
<img alt="" src="http://stackoverflow.com/feeds/tag/images/flv.jpg" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" class="blue_font" value="flv" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/mp4.jpg" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="mp4" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/wmv.png" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="wmv" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/avi.png" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="avi" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/mp3.png" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="mp3" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/ogg.png" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="ogg" />
<img alt="" src="http://stackoverflow.com/feeds/tag/images/webm.png" class="format_button" style='max-width: 300px; max-height: 300px' />
<input type="radio" value="webm" />
</div>
</fieldset>
<fieldset>
<legend class="show_legend">Optional settings</legend>
<div class="radio_buttons" style="display:inline;padding:10px">
<br />Video quality:
<select class="radio_buttons" disabled="disabled">
<option value="2000000">high</option>
<option value="logo.png -filter_complex 'overlay' ">medium</option>
<option value="-i seen.png \-filter_complex '[0:v][1:v]overlay=10:10:enable=between(t\,15\,25)' -c:v libx264 -pix_fmt yuv420p -movflags faststart">Seen</option>
<option value="-i logo2.png \-filter_complex '[0:v][1:v]overlay=10:10:enable=between(t\,10\,20)'">HD Watermark</option>
<option value="-i logo.png -filter_complex 'overlay'">Watermark</option>
<option value="-vf drawtext='fontsize=20:y=h-line_h-10:x=(2*n)-tw:fontfile='op.ttf':text='DesiBombs.com'' -c:v libx264 -pix_fmt yuv420p -movflags faststart">Watermark</option>
</select>
</div>
<div class="radio_buttons" style="display:inline;padding:10px">Audio quality:
<select class="radio_buttons" disabled="disabled">
<option value="44100">high</option>
<option value="22050">medium</option>
<option value="11025">low</option>
</select>
<div class="radio_buttons" style="display:inline;padding:10px">Video size:
<select class="radio_buttons" disabled="disabled">
<option value="320x240">320x240</option>
<option value="512x384" selected="selected">512x384</option>
<option value="640x360">640x360</option>
<option value="854x480">854x480</option>
<option value="1280x720">1280x720</option>
</select>
</div>
</div></fieldset>
<div class="radio_buttons">
<input type="submit" disabled="disabled" value="" style="display:none" />
</div>
</form></center>
<?php
}
elseif(isset($_POST['type']))
{
$new_format=htmlspecialchars($_POST['type']); // read output format from form
$name=$_SESSION['name'];
//$_SESSION['extension'];
$video_to_convert=$store_dir.$_SESSION['name'].$_SESSION['extension'];
if (isset($_POST['size'])) {$size=$_POST['size'];}
if (isset($_POST['quality'])) {$quality=$_POST['quality'];}
if (isset($_POST['audio'])) {$audio=$_POST['audio'];}
if($new_format=="webm" && $audio=="11025"){$audio="22050";$new_format="webm"; }// for webm and ogg audio can not be 11050
if($new_format=="ogg" && $audio=="11025"){$audio="22050"; $new_format="ogg"; }// for webm and ogg audio can not be 11050
$_SESSION['type']=$new_format;
if($new_format=="flv"){ $call=$ffmpeg." -i ".$video_to_convert." -vcodec flv -f flv -r 30 -b ".$quality."-ab 128000 -ar".$audio." -s ".$size." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="avi"){ $call=$ffmpeg." -i ".$video_to_convert." -vcodec mjpeg -f avi -acodec libmp3lame -b ".$quality." -s ".$size." -r 30 -g 12 -qmin 3 -qmax 13 -ab 224 -ar ".$audio." -ac 2 ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="mp3"){ $call=$ffmpeg." -i ".$video_to_convert." -vn -acodec libmp3lame -ac 2 -ab 128000 -ar ".$audio." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="mp4"){ $call=$ffmpeg." -i ".$video_to_convert." -vf logo.png -vcodec mpeg4 -r 30 -b ".$quality." -acodec libmp3lame -ab 126000 -ar ".$audio." -ac 2 -s ".$size." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="wmv"){ $call=$ffmpeg." -i ".$video_to_convert." -vcodec wmv1 -r 30 -b ".$quality." -acodec wmav2 -ab 128000 -ar ".$audio." -ac 2 -s ".$size." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="ogg"){ $call=$ffmpeg." -i ".$video_to_convert." -vcodec libtheora -r 30 -b ".$quality." -acodec libvorbis -ab 128000 -ar ".$audio." -ac 2 -s ".$size." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
if($new_format=="webm"){ $call=$ffmpeg." -i ".$video_to_convert." -vcodec libvpx -r 30 -b ".$quality." -acodec libvorbis -ab 128000 -ar ".$audio." -ac 2 -s ".$size." ".$converted_vids.$name.".".$new_format." -y 2> log/".$name.".txt";}
/* START CONVERTING The Video */
require_once("includes/ffmpeg_cmd.php");
$convert = (popen($convert_call, "r"));
pclose($convert);
flush();
// define sessions
$_SESSION['dest_file']=$converted_vids.$name.".".$new_format;
echo "<code class="echappe-js"><script>\n";<br />
echo "counter = 5\n";<br />
echo "var redirect = 0; \n";<br />
echo "$(document).ready(function()\n";<br />
echo "{\n";<br />
echo "var refreshId = setInterval(function() \n";<br />
echo "{\n";<br />
echo "if(redirect == 0) \n { \n";<br />
echo "$('#timeval').load('progress_bar.php');\n";<br />
echo "}\n";<br />
echo "else \n";<br />
echo "{\n";<br />
echo "$('#timeval').load('progress_bar.php?counter='+ counter--);\n";<br />
echo "}\n";<br />
echo "}, 1000);\n";<br />
echo "});\n"; <br />
echo "</script>\n" ;
//
/************************* for debug *************************************
echo "" ; print_r ($_SESSION) ; echo "
" ; **********************************************************************************/
?>
< ?php
else if(isset($_GET[’finished’]))
echo "" ; if(!isset($_SESSION[’name’])) echo "please convert new video" ;include("templates/footer.php") ; ;exit ; $filepath=$converted_vids."./".$_SESSION[’name’].".".$_SESSION[’type’] ; require_once"video_duration.php" ;
$_SESSION[’duration’]=$hours." :".$minutes." :".$seconds ;
$vid_name=$_SESSION[’name’] ;
$vid_src=$_SESSION[’extension’] ;
$vid_new=$_SESSION[’type’] ;
if (isset($_SESSION[’duration’])) $duration=$_SESSION[’duration’] ;else $duration="unknown" ;
if(file_exists($converted_vids.$vid_name.".".$vid_new))
$vid_size=get_size($converted_vids.$vid_name.".".$vid_new) ;
else $vid_size=0 ;?>
your video informationVideo duration < ?php echo $hours." :".$minutes." :".$seconds ; ?> original video name < ?php if(isset($_SESSION[’original_name’]))echo $_SESSION[’original_name’] ; ?> video size < ?php echo $vid_size." MB
" ; ?>source extension < ?php echo str_replace(".","","$vid_src") ; ?> new extension < ?php echo $vid_new ; ?> < ?php
echo "
" ;
session_unset() ;
else
?><script type="text/javascript"><br />
// Convert divs to queue widgets when the DOM is ready<br />
$(function() {<br />
$("#uploader").pluploadQueue({<br />
// General settings<br />
runtimes : 'flash',<br />
url : 'upload.php',<br />
max_file_size : '&lt;?php echo $max_file_size ;?>mb',<br />
chunk_size : '1mb',<br />
unique_names : true,<br />
<br />
// Resize images on clientside if we can<br />
resize : {width : 320, height : 240, quality : 90},<br />
init : {<br />
FilesAdded: function(up, files) {<br />
plupload.each(files, function(file) {<br />
if (up.files.length > 1) {<br />
up.removeFile(file);<br />
}<br />
});<br />
if (up.files.length >= 1) {<br />
$('#pickfiles').hide('slow');<br />
}<br />
},<br />
FilesRemoved: function(up, files) {<br />
if (up.files.length < 1) {<br />
$('#pickfiles').fadeIn('slow');<br />
}<br />
}<br />
},<br />
multi_selection: false,<br />
// Specify what files to browse for<br />
filters : [<br />
{title : "Video files", extensions : "&lt;?php foreach ($allowedExtensions as $value) { echo $value . ","; }?>"}<br />
<br />
],<br />
<br />
// Flash settings<br />
flash_swf_url : 'js/plupload.flash.swf',<br />
<br />
// Silverlight settings<br />
silverlight_xap_url : 'js/plupload.silverlight.xap'<br />
});<br />
<br />
// Client side form validation<br />
$('form').submit(function(e) {<br />
var uploader = $('#uploader').pluploadQueue();<br />
<br />
// Files in queue upload them first<br />
if (uploader.files.length > 0) {<br />
// When all files are uploaded submit form<br />
uploader.bind('StateChanged', function() {<br />
if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) {<br />
$('form')[0].submit();<br />
}<br />
});<br />
<br />
uploader.start();<br />
} else {<br />
alert('You must queue at least one video.');<br />
}<br />
<br />
return false;<br />
});<br />
});<br />
</script>Maximum upload size < ?php echo $max_file_size ; ?> MB
< ?php include("templates/info.php") ; ?>
< ?php
//End Body Area/
?>
ffmpeg/libx264 C API : frames dropped from end of short MP4
In my C++ application, I am taking a series of JPEG images, manipulating their data using FreeImage, and then encoding the bitmaps as H264 using the ffmpeg/libx264 C API. The output is an MP4 which shows the series of 22 images at 12fps. My code is adapted from the "muxing" example that comes with ffmpeg C source code.
My problem : no matter how I tune the codec parameters, a certain number of frames at the end of the sequence which are passed to the encoder do not appear in the final output. I’ve set the AVCodecContext parameters like this :
//set context params
ctx->codec_id = AV_CODEC_ID_H264;
ctx->bit_rate = 4000 * 1000;
ctx->width = _width;
ctx->height = _height;
ost->st->time_base = AVRational{ 1, 12 };
ctx->time_base = ost->st->time_base;
ctx->gop_size = 1;
ctx->pix_fmt = AV_PIX_FMT_YUV420P;
I have found that the higher the gop_size
the more frames are dropped from the end of the video. I can also see from the output that, with this gop size (where I’m essentially directing that all output frames be I frames) that only 9 frames are written.
I’m not sure why this is occurring. I experimented with encoding duplicate frames and making a much longer video. This resulted in no frames being dropped. I know with the ffmpeg command line tool there is a concatenation command that accomplishes what I am trying to do, but I’m not sure how to accomplish the same goal using the C API.
Here’s the output I’m getting from the console :
[libx264 @ 026d81c0] using cpu capabilities : MMX2 SSE2Fast SSSE3
SSE4.2 AVX FMA3 BMI2 AVX2 [libx264 @ 026d81c0] profile High, level
3.1 [libx264 @ 026d81c0] 264 - core 152 r2851 ba24899 - H.264/MPEG-4 AVC codec - Cop yleft 2003-2017 - http://www.videolan.org/x264.html -
options : cabac=1 ref=1 deb lock=1:0:0 analyse=0x3:0x113 me=hex subme=7
psy=1 psy_rd=1.00:0.00 mixed_ref=0 m e_range=16 chroma_me=1 trellis=1
8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chro ma_qp_offset=-2
threads=12 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1
interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0
keyint=1 ke yint_min=1 scenecut=40 intra_refresh=0 rc=abr mbtree=0
bitrate=4000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4
ip_ratio=1.40 aq=1:1.00 Output #0, mp4, to
’....\images\c411a991-46f6-400c-8bb0-77af3738559a.mp4’ :
Stream #0:0 : Video : h264, yuv420p, 700x700, q=2-31, 4000 kb/s, 12 tbn[libx264 @ 026d81c0] frame I:9 Avg QP:17.83 size:111058 [libx264
@ 026d81c0] mb I I16..4 : 1.9% 47.7% 50.5% [libx264 @ 026d81c0] final
ratefactor : 19.14 [libx264 @ 026d81c0] 8x8 transform intra:47.7%
[libx264 @ 026d81c0] coded y,uvDC,uvAC intra : 98.4% 96.9% 89.5%
[libx264 @ 026d81c0] i16 v,h,dc,p : 64% 6% 2% 28% [libx264 @
026d81c0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu : 32% 15% 9% 5% 5% 6% 8%
10% 10% [libx264 @ 026d81c0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu : 28% 18%
7% 6% 8% 8% 8% 9% 8% [libx264 @ 026d81c0] i8c dc,h,v,p : 43% 22%
25% 10% [libx264 @ 026d81c0] kb/s:10661.53
Code included below :
MP4Writer.h
#ifndef MPEG_WRITER
#define MPEG_WRITER
#include <iostream>
#include <string>
#include <vector>
#include
extern "C" {
#include <libavformat></libavformat>avformat.h>
#include <libswscale></libswscale>swscale.h>
#include <libswresample></libswresample>swresample.h>
#include <libswscale></libswscale>swscale.h>
}
typedef struct OutputStream
{
AVStream *st;
AVCodecContext *enc;
//pts of the next frame that will be generated
int64_t next_pts;
int samples_count;
AVFrame *frame;
AVFrame *tmp_frame;
float t, tincr, tincr2;
struct SwsContext *sws_ctx;
struct SwrContext *swr_ctx;
};
class MP4Writer {
public:
MP4Writer();
void Init();
int16_t SetOutput( const std::string & path );
int16_t AddFrame( uint8_t * imgData );
int16_t Write( std::vector<imgdata> & imgData );
int16_t Finalize();
void SetHeight( const int height ) { _height = _width = height; } //assuming 1:1 aspect ratio
private:
int16_t AddStream( OutputStream * ost, AVFormatContext * formatCtx, AVCodec ** codec, enum AVCodecID codecId );
int16_t OpenVideo( AVFormatContext * formatCtx, AVCodec *codec, OutputStream * ost, AVDictionary * optArg );
static AVFrame * AllocPicture( enum AVPixelFormat pixFmt, int width, int height );
static AVFrame * GetVideoFrame( uint8_t * imgData, OutputStream * ost, const int width, const int height );
static int WriteFrame( AVFormatContext * formatCtx, const AVRational * timeBase, AVStream * stream, AVPacket * packet );
int _width;
int _height;
OutputStream _ost;
AVFormatContext * _formatCtx;
AVDictionary * _dict;
};
#endif //MPEG_WRITER
</imgdata></vector></string></iostream>
MP4Writer.cpp
#include
#include <algorithm>
MP4Writer::MP4Writer()
{
_width = 0;
_height = 0;
}
void MP4Writer::Init()
{
av_register_all();
}
/**
sets up output stream for the specified path.
note that the output format is deduced automatically from the file extension passed
@param path: output file path
@returns: -1 = output could not be deduced, -2 = invalid codec, -3 = error opening output file,
-4 = error writing header
*/
int16_t MP4Writer::SetOutput( const std::string & path )
{
int error;
AVCodec * codec;
AVOutputFormat * format;
_ost = OutputStream{}; //TODO reset state in a more focused way?
//allocate output media context
avformat_alloc_output_context2( &_formatCtx, NULL, NULL, path.c_str() );
if ( !_formatCtx ) {
std::cout << "could not deduce output format from file extension. aborting" << std::endl;
return -1;
}
//set format
format = _formatCtx->oformat;
if ( format->video_codec != AV_CODEC_ID_NONE ) {
AddStream( &_ost, _formatCtx, &codec, format->video_codec );
}
else {
std::cout << "there is no video codec set. aborting" << std::endl;
return -2;
}
OpenVideo( _formatCtx, codec, &_ost, _dict );
av_dump_format( _formatCtx, 0, path.c_str(), 1 );
//open output file
if ( !( format->flags & AVFMT_NOFILE )) {
error = avio_open( &_formatCtx->pb, path.c_str(), AVIO_FLAG_WRITE );
if ( error < 0 ) {
std::cout << "there was an error opening output file " << path << ". aborting" << std::endl;
return -3;
}
}
//write header
error = avformat_write_header( _formatCtx, &_dict );
if ( error < 0 ) {
std::cout << "an error occurred writing header. aborting" << std::endl;
return -4;
}
return 0;
}
/**
initialize the output stream
@param ost: the output stream
@param formatCtx: the context format
@param codec: the output codec
@param codec: the ffmpeg enumerated id of the codec
@returns: -1 = encoder not found, -2 = stream could not be allocated, -3 = encoding context could not be allocated
*/
int16_t MP4Writer::AddStream( OutputStream * ost, AVFormatContext * formatCtx, AVCodec ** codec, enum AVCodecID codecId )
{
AVCodecContext * ctx; //TODO not sure why this is here, could just set ost->enc directly
int i;
//detect the encoder
*codec = avcodec_find_encoder( codecId );
if ( (*codec) == NULL ) {
std::cout << "could not find encoder. aborting" << std::endl;
return -1;
}
//allocate stream
ost->st = avformat_new_stream( formatCtx, NULL );
if ( ost->st == NULL ) {
std::cout << "could not allocate stream. aborting" << std::endl;
return -2;
}
//allocate encoding context
ost->st->id = formatCtx->nb_streams - 1;
ctx = avcodec_alloc_context3( *codec );
if ( ctx == NULL ) {
std::cout << "could not allocate encoding context. aborting" << std::endl;
return -3;
}
ost->enc = ctx;
//set context params
ctx->codec_id = AV_CODEC_ID_H264;
ctx->bit_rate = 4000 * 1000;
ctx->width = _width;
ctx->height = _height;
ost->st->time_base = AVRational{ 1, 12 };
ctx->time_base = ost->st->time_base;
ctx->gop_size = 1;
ctx->pix_fmt = AV_PIX_FMT_YUV420P;
//if neccesary, set stream headers and formats separately
if ( formatCtx->oformat->flags & AVFMT_GLOBALHEADER ) {
std::cout << "setting stream and headers to be separate" << std::endl;
ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
return 0;
}
/**
open the video for writing
@param formatCtx: the format context
@param codec: output codec
@param ost: output stream
@param optArg: dictionary
@return: -1 = error opening codec, -2 = allocate new frame, -3 = copy stream params
*/
int16_t MP4Writer::OpenVideo( AVFormatContext * formatCtx, AVCodec *codec, OutputStream * ost, AVDictionary * optArg )
{
int error;
AVCodecContext * ctx = ost->enc;
AVDictionary * dict = NULL;
av_dict_copy( &dict, optArg, 0 );
//open codec
error = avcodec_open2( ctx, codec, &dict );
av_dict_free( &dict );
if ( error < 0 ) {
std::cout << "there was an error opening the codec. aborting" << std::endl;
return -1;
}
//allocate new frame
ost->frame = AllocPicture( ctx->pix_fmt, ctx->width, ctx->height );
if ( ost->frame == NULL ) {
std::cout << "there was an error allocating a new frame. aborting" << std::endl;
return -2;
}
//copy steam params
error = avcodec_parameters_from_context( ost->st->codecpar, ctx );
if ( error < 0 ) {
std::cout << "could not copy stream parameters. aborting" << std::endl;
return -3;
}
return 0;
}
/**
allocate a new frame
@param pixFmt: ffmpeg enumerated pixel format
@param width: output width
@param height: output height
@returns: an inititalized frame
*/
AVFrame * MP4Writer::AllocPicture( enum AVPixelFormat pixFmt, int width, int height )
{
AVFrame * picture;
int error;
//allocate the frame
picture = av_frame_alloc();
if ( picture == NULL ) {
std::cout << "there was an error allocating the picture" << std::endl;
return NULL;
}
picture->format = pixFmt;
picture->width = width;
picture->height = height;
//allocate the frame's data buffer
error = av_frame_get_buffer( picture, 32 );
if ( error < 0 ) {
std::cout << "could not allocate frame data" << std::endl;
return NULL;
}
picture->pts = 0;
return picture;
}
/**
convert raw RGB buffer to YUV frame
@return: frame that contains image data
*/
AVFrame * MP4Writer::GetVideoFrame( uint8_t * imgData, OutputStream * ost, const int width, const int height )
{
int error;
AVCodecContext * ctx = ost->enc;
//prepare the frame
error = av_frame_make_writable( ost->frame );
if ( error < 0 ) {
std::cout << "could not make frame writeable" << std::endl;
return NULL;
}
//TODO set this context one time per run, or even better, one time at init
//convert RGB to YUV
struct SwsContext* fooContext = sws_getContext( width, height, AV_PIX_FMT_BGR24,
width, height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL );
int inLinesize[1] = { 3 * width }; // RGB stride
uint8_t * inData[1] = { imgData };
int sliceHeight = sws_scale( fooContext, inData, inLinesize, 0, height, ost->frame->data, ost->frame->linesize );
sws_freeContext( fooContext );
ost->frame->pts = ost->next_pts++;
//TODO does the frame need to be returned here as it is available at the class level?
return ost->frame;
}
/**
write frame to file
@param formatCtx: the output format context
@param timeBase: the framerate
@param stream: output stream
@param packet: data packet
@returns: see return values for av_interleaved_write_frame
*/
int MP4Writer::WriteFrame( AVFormatContext * formatCtx, const AVRational * timeBase, AVStream * stream, AVPacket * packet )
{
av_packet_rescale_ts( packet, *timeBase, stream->time_base );
packet->stream_index = stream->index;
//write compressed file to media file
return av_interleaved_write_frame( formatCtx, packet );
}
int16_t MP4Writer::Write( std::vector<imgdata> & imgData )
{
int16_t errorCount = 0;
int16_t retVal = 0;
bool countingUp = true;
size_t i = 0;
while ( true ) {
//don't show first frame again when counting back down
if ( !countingUp && i == 0 ) {
break;
}
uint8_t * pixels = imgData[i].GetBits( imgData[i].mp4Input );
AddFrame( pixels );
//handle inc/dec without repeating last frame
if ( countingUp ) {
if ( i == imgData.size() -1 ) {
countingUp = false;
i--;
}
else {
i++;
}
}
else {
i--;
}
}
Finalize();
return 0; //TODO return error code
}
/**
add another frame to output video
@param imgData: the raw image data
@returns -1 = error encoding video frame, -2 = error writing frame
*/
int16_t MP4Writer::AddFrame( uint8_t * imgData )
{
int error;
AVCodecContext * ctx;
AVFrame * frame;
int gotPacket = 0;
AVPacket pkt = { 0 };
ctx = _ost.enc;
av_init_packet( &pkt );
frame = GetVideoFrame( imgData, &_ost, _width, _height );
//encode the image
error = avcodec_encode_video2( ctx, &pkt, frame, &gotPacket );
if ( error < 0 ) {
std::cout << "there was an error encoding the video frame" << std::endl;
return -1;
}
//write the frame. NOTE: this doesn't kick in until the encoder has received a certain number of frames
if ( gotPacket ) {
error = WriteFrame( _formatCtx, &ctx->time_base, _ost.st, &pkt );
if ( error < 0 ) {
std::cout << "the video frame could not be written" << std::endl;
return -2;
}
}
return 0;
}
/**
finalize output video and cleanup
*/
int16_t MP4Writer::Finalize()
{
av_write_trailer( _formatCtx );
avcodec_free_context( &_ost.enc );
av_frame_free( &_ost.frame);
av_frame_free( &_ost.tmp_frame );
avio_closep( &_formatCtx->pb );
avformat_free_context( _formatCtx );
sws_freeContext( _ost.sws_ctx );
swr_free( &_ost.swr_ctx);
return 0;
}
</imgdata></algorithm>
usage
#include
#include
#include <vector>
struct ImgData
{
unsigned int width;
unsigned int height;
std::string path;
FIBITMAP * mp4Input;
uint8_t * GetBits( FIBITMAP * bmp ) { return FreeImage_GetBits( bmp ); }
};
int main()
{
std::vector<imgdata> imgDataVec;
//load images and push to imgDataVec
MP4Writer mp4Writer;
mp4Writer.SetHeight( 1200 ); //assumes 1:1 aspect ratio
mp4Writer.Init();
mp4Writer.SetOutput( "test.mp4" );
mp4Writer.Write( imgDataVec );
}
</imgdata></vector>