From ac57e2df132d4ea05dacee642de74d057bd98f5d Mon Sep 17 00:00:00 2001 From: Karl Kroening <karlk@kralnet.us> Date: Sun, 20 May 2018 01:13:07 -0700 Subject: [PATCH 1/5] Add input/output support in `run` command; update docs --- .gitignore | 1 + doc/html/genindex.html | 22 ++++++++++ doc/html/index.html | 83 +++++++++++++++++++++++++++++++++--- doc/html/objects.inv | Bin 398 -> 427 bytes doc/html/searchindex.js | 2 +- ffmpeg/_ffmpeg.py | 13 +++++- ffmpeg/_probe.py | 16 +++---- ffmpeg/_run.py | 67 +++++++++++++++++++++++------ ffmpeg/tests/test_ffmpeg.py | 67 +++++++++++++++++++++++------ requirements-base.txt | 1 + requirements.txt | 8 ++++ setup.py | 2 +- 12 files changed, 238 insertions(+), 44 deletions(-) mode change 100755 => 100644 ffmpeg/_probe.py diff --git a/.gitignore b/.gitignore index 3179ebc..780f20e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ dist/ ffmpeg/tests/sample_data/out*.mp4 ffmpeg_python.egg-info/ venv* +build/ diff --git a/doc/html/genindex.html b/doc/html/genindex.html index c39ba30..4e258ee 100644 --- a/doc/html/genindex.html +++ b/doc/html/genindex.html @@ -50,12 +50,14 @@ <div class="genindex-jumpbox"> <a href="#C"><strong>C</strong></a> | <a href="#D"><strong>D</strong></a> + | <a href="#E"><strong>E</strong></a> | <a href="#F"><strong>F</strong></a> | <a href="#G"><strong>G</strong></a> | <a href="#H"><strong>H</strong></a> | <a href="#I"><strong>I</strong></a> | <a href="#M"><strong>M</strong></a> | <a href="#O"><strong>O</strong></a> + | <a href="#P"><strong>P</strong></a> | <a href="#R"><strong>R</strong></a> | <a href="#S"><strong>S</strong></a> | <a href="#T"><strong>T</strong></a> @@ -67,6 +69,8 @@ <table style="width: 100%" class="indextable genindextable"><tr> <td style="width: 33%; vertical-align: top;"><ul> <li><a href="index.html#ffmpeg.colorchannelmixer">colorchannelmixer() (in module ffmpeg)</a> +</li> + <li><a href="index.html#ffmpeg.compile">compile() (in module ffmpeg)</a> </li> </ul></td> <td style="width: 33%; vertical-align: top;"><ul> @@ -89,6 +93,14 @@ </ul></td> </tr></table> +<h2 id="E">E</h2> +<table style="width: 100%" class="indextable genindextable"><tr> + <td style="width: 33%; vertical-align: top;"><ul> + <li><a href="index.html#ffmpeg.Error">Error</a> +</li> + </ul></td> +</tr></table> + <h2 id="F">F</h2> <table style="width: 100%" class="indextable genindextable"><tr> <td style="width: 33%; vertical-align: top;"><ul> @@ -97,6 +109,8 @@ </ul></td> <td style="width: 33%; vertical-align: top;"><ul> <li><a href="index.html#ffmpeg.filter_">filter_() (in module ffmpeg)</a> +</li> + <li><a href="index.html#ffmpeg.filter_multi_output">filter_multi_output() (in module ffmpeg)</a> </li> </ul></td> </tr></table> @@ -151,6 +165,14 @@ </ul></td> </tr></table> +<h2 id="P">P</h2> +<table style="width: 100%" class="indextable genindextable"><tr> + <td style="width: 33%; vertical-align: top;"><ul> + <li><a href="index.html#ffmpeg.probe">probe() (in module ffmpeg)</a> +</li> + </ul></td> +</tr></table> + <h2 id="R">R</h2> <table style="width: 100%" class="indextable genindextable"><tr> <td style="width: 33%; vertical-align: top;"><ul> diff --git a/doc/html/index.html b/doc/html/index.html index c7717a6..ba91983 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -313,6 +313,21 @@ and arguments to ffmpeg verbatim.</p> <p><code class="docutils literal"><span class="pre">ffmpeg.input('in.mp4').filter_('hflip').output('out.mp4').run()</span></code></p> </dd></dl> +<dl class="function"> +<dt id="ffmpeg.filter_multi_output"> +<code class="descclassname">ffmpeg.</code><code class="descname">filter_multi_output</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>filter_name</em>, <em>*args</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.filter_multi_output" title="Permalink to this definition">¶</a></dt> +<dd><p>Apply custom filter with one or more outputs.</p> +<p>This is the same as <code class="docutils literal"><span class="pre">filter_</span></code> except that the filter can produce more than one output.</p> +<p>To reference an output stream, use either the <code class="docutils literal"><span class="pre">.stream</span></code> operator or bracket shorthand:</p> +<p class="rubric">Example</p> +<p><code class="docutils literal"><span class="pre">`</span> +<span class="pre">split</span> <span class="pre">=</span> <span class="pre">ffmpeg.input('in.mp4').filter_multi_output('split')</span> +<span class="pre">split0</span> <span class="pre">=</span> <span class="pre">split.stream(0)</span> +<span class="pre">split1</span> <span class="pre">=</span> <span class="pre">split[1]</span> +<span class="pre">ffmpeg.concat(split0,</span> <span class="pre">split1).output('out.mp4').run()</span> +<span class="pre">`</span></code></p> +</dd></dl> + <dl class="function"> <dt id="ffmpeg.hflip"> <code class="descclassname">ffmpeg.</code><code class="descname">hflip</code><span class="sig-paren">(</span><em>stream</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.hflip" title="Permalink to this definition">¶</a></dt> @@ -479,6 +494,9 @@ for single input image.</li> <dt id="ffmpeg.input"> <code class="descclassname">ffmpeg.</code><code class="descname">input</code><span class="sig-paren">(</span><em>filename</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.input" title="Permalink to this definition">¶</a></dt> <dd><p>Input file URL (ffmpeg <code class="docutils literal"><span class="pre">-i</span></code> option)</p> +<p>Any supplied kwargs are passed to ffmpeg verbatim (e.g. <code class="docutils literal"><span class="pre">t=20</span></code>, +<code class="docutils literal"><span class="pre">f='mp4'</span></code>, <code class="docutils literal"><span class="pre">acodec='pcm'</span></code>, etc.).</p> +<p>To tell ffmpeg to read from stdin, use <code class="docutils literal"><span class="pre">pipe:</span></code> as the filename.</p> <p>Official documentation: <a class="reference external" href="https://ffmpeg.org/ffmpeg.html#Main-options">Main options</a></p> </dd></dl> @@ -490,8 +508,17 @@ for single input image.</li> <dl class="function"> <dt id="ffmpeg.output"> -<code class="descclassname">ffmpeg.</code><code class="descname">output</code><span class="sig-paren">(</span><em>stream</em>, <em>filename</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.output" title="Permalink to this definition">¶</a></dt> +<code class="descclassname">ffmpeg.</code><code class="descname">output</code><span class="sig-paren">(</span><em>*streams_and_filename</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.output" title="Permalink to this definition">¶</a></dt> <dd><p>Output file URL</p> +<dl class="docutils"> +<dt>Syntax:</dt> +<dd><cite>ffmpeg.output(stream1[, stream2, stream3…], filename, **ffmpeg_args)</cite></dd> +</dl> +<p>If multiple streams are provided, they are mapped to the same +output.</p> +<p>Any supplied kwargs are passed to ffmpeg verbatim (e.g. <code class="docutils literal"><span class="pre">t=20</span></code>, +<code class="docutils literal"><span class="pre">f='mp4'</span></code>, <code class="docutils literal"><span class="pre">acodec='pcm'</span></code>, etc.).</p> +<p>To tell ffmpeg to write to stdout, use <code class="docutils literal"><span class="pre">pipe:</span></code> as the filename.</p> <p>Official documentation: <a class="reference external" href="https://ffmpeg.org/ffmpeg.html#Synopsis">Synopsis</a></p> </dd></dl> @@ -502,24 +529,53 @@ for single input image.</li> <p>Official documentation: <a class="reference external" href="https://ffmpeg.org/ffmpeg.html#Main-options">Main options</a></p> </dd></dl> +<dl class="function"> +<dt id="ffmpeg.compile"> +<code class="descclassname">ffmpeg.</code><code class="descname">compile</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>cmd=u'ffmpeg'</em>, <em>overwrite_output=False</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.compile" title="Permalink to this definition">¶</a></dt> +<dd><p>Build command-line for invoking ffmpeg.</p> +<p>The <a class="reference internal" href="#ffmpeg.run" title="ffmpeg.run"><code class="xref py py-meth docutils literal"><span class="pre">run()</span></code></a> function uses this to build the commnad line +arguments and should work in most cases, but calling this function +directly is useful for debugging or if you need to invoke ffmpeg +manually for whatever reason.</p> +<p>This is the same as calling <a class="reference internal" href="#ffmpeg.get_args" title="ffmpeg.get_args"><code class="xref py py-meth docutils literal"><span class="pre">get_args()</span></code></a> except that it also +includes the <code class="docutils literal"><span class="pre">ffmpeg</span></code> command as the first argument.</p> +</dd></dl> + +<dl class="exception"> +<dt id="ffmpeg.Error"> +<em class="property">exception </em><code class="descclassname">ffmpeg.</code><code class="descname">Error</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>stdout</em>, <em>stderr</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.Error" title="Permalink to this definition">¶</a></dt> +<dd><p>Bases: <code class="xref py py-class docutils literal"><span class="pre">exceptions.Exception</span></code></p> +</dd></dl> + <dl class="function"> <dt id="ffmpeg.get_args"> <code class="descclassname">ffmpeg.</code><code class="descname">get_args</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>overwrite_output=False</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.get_args" title="Permalink to this definition">¶</a></dt> -<dd><p>Get command-line arguments for ffmpeg.</p> +<dd><p>Build command-line arguments to be passed to ffmpeg.</p> </dd></dl> <dl class="function"> <dt id="ffmpeg.run"> -<code class="descclassname">ffmpeg.</code><code class="descname">run</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>cmd=u'ffmpeg'</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.run" title="Permalink to this definition">¶</a></dt> -<dd><p>Run ffmpeg on node graph.</p> +<code class="descclassname">ffmpeg.</code><code class="descname">run</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>cmd=u'ffmpeg'</em>, <em>capture_stdout=False</em>, <em>capture_stderr=False</em>, <em>input=None</em>, <em>quiet=False</em>, <em>overwrite_output=False</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.run" title="Permalink to this definition">¶</a></dt> +<dd><p>Ivoke ffmpeg for the supplied node graph.</p> <table class="docutils field-list" frame="void" rules="none"> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>**kwargs</strong> – keyword-arguments passed to <code class="docutils literal"><span class="pre">get_args()</span></code> (e.g. <code class="docutils literal"><span class="pre">overwrite_output=True</span></code>).</td> +<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> +<li><strong>capture_stdout</strong> – if True, capture stdout (to be used with +<code class="docutils literal"><span class="pre">pipe:</span></code> ffmpeg outputs).</li> +<li><strong>capture_stderr</strong> – if True, capture stderr.</li> +<li><strong>quiet</strong> – shorthand for setting <code class="docutils literal"><span class="pre">capture_stdout</span></code> and <code class="docutils literal"><span class="pre">capture_stderr</span></code>.</li> +<li><strong>input</strong> – text to be sent to stdin (to be used with <code class="docutils literal"><span class="pre">pipe:</span></code> +ffmpeg inputs)</li> +<li><strong>**kwargs</strong> – keyword-arguments passed to <code class="docutils literal"><span class="pre">get_args()</span></code> (e.g. +<code class="docutils literal"><span class="pre">overwrite_output=True</span></code>).</li> +</ul> +</td> </tr> </tbody> </table> +<p>Returns: (out, err) tuple containing captured stdout and stderr data.</p> </dd></dl> <dl class="function"> @@ -527,6 +583,23 @@ for single input image.</li> <code class="descclassname">ffmpeg.</code><code class="descname">view</code><span class="sig-paren">(</span><em>stream_spec</em>, <em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.view" title="Permalink to this definition">¶</a></dt> <dd></dd></dl> +<dl class="function"> +<dt id="ffmpeg.probe"> +<code class="descclassname">ffmpeg.</code><code class="descname">probe</code><span class="sig-paren">(</span><em>filename</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.probe" title="Permalink to this definition">¶</a></dt> +<dd><p>Run ffprobe on the specified file and return a JSON representation of the output.</p> +<table class="docutils field-list" frame="void" rules="none"> +<col class="field-name" /> +<col class="field-body" /> +<tbody valign="top"> +<tr class="field-odd field"><th class="field-name">Raises:</th><td class="field-body"><a class="reference internal" href="#ffmpeg.Error" title="ffmpeg.Error"><code class="xref py py-class docutils literal"><span class="pre">ffmpeg.Error</span></code></a> – if ffprobe returns a non-zero exit code, +an <a class="reference internal" href="#ffmpeg.Error" title="ffmpeg.Error"><code class="xref py py-class docutils literal"><span class="pre">Error</span></code></a> is returned with a generic error message. +The stderr output can be retrieved by accessing the +<code class="docutils literal"><span class="pre">stderr</span></code> property of the exception.</td> +</tr> +</tbody> +</table> +</dd></dl> + </div> <div class="section" id="indices-and-tables"> <h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1> diff --git a/doc/html/objects.inv b/doc/html/objects.inv index 49e13ed09c61f4724332794f7da26e5aa7e62699..1072aecde4a2efc3029fc6e2136fd1873ccda6a9 100644 GIT binary patch delta 313 zcmV-90mlB01FHj&dw-Rb!D_=W42JJ{3d43yS+BjXu*0CR2MA6gCkERxirglB`Z@8+ zIv7+v*+`!(>9;Loe1t*8<>0YT4%C~np@(@lvUe}VR==vcG8?#iCqi0rm^+AUF{u50 z#2wn9L+VBy0yuAH*uqQ--Ge9}ZLUMchEB5#C5$4lCw*yg7Jsu0v#bJR9RoGbus%7q z4JM9BmSX^JG!2so9*wgyO~xd<4;!R~dY}P*I+IoVgbH{Da(WT77qSe`$Q%h<nIe>e zh?{_;PzWaLMc`R{zh?^<5qQP?JN1K>1_+x)P23-xZo#Q~uWr_jQy=TL`o((eR_Kl> z8SA+4h~c14+cK)!OxTbgxXDxKcwCVx52=6h#ihPE%7LF{zLm@q=scSKMig6tzl#5) L2dKUQZAQzNXb_U# delta 284 zcmV+%0ptFw1C9fbdw-48&1!@&5C`zPPchJ5UAk8vV4;V?LLWe)PBf4&#)%*M^i_j& z7Ydz|GWq?T3~ID?5qpSh<wBP%!Uq`N;c^++*|%IP-=Qq@0WWQ^f!aZNkJgz5X^-mU zTQ<SN^*Q{K@~vVSr4U81BQ-Z+5nIMZwgYV}Bh@0?BiD-hQGbL7ZH;WUA)&|_ACqh- zhty*o5^n+7ZnKrq3CXHeY-cijZblTM<V9{oj>7Snj1$30{MIIevj{#ya8dm;Qjh+q zz{p)?Rf87FE1cEC!RLNk{IV{zJ9-kiV;NT-g0J9yT%p;7@!Mys1HtXKlVbbV|L7Oj i@?6Ld{L05f?iD<u+75%)M6PB0o!9TG$KnS}CyJ|u&yF$x diff --git a/doc/html/searchindex.js b/doc/html/searchindex.js index cfde87c..d86eec7 100644 --- a/doc/html/searchindex.js +++ b/doc/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["index"],envversion:52,filenames:["index.rst"],objects:{"":{ffmpeg:[0,0,0,"-"]},ffmpeg:{colorchannelmixer:[0,1,1,""],concat:[0,1,1,""],crop:[0,1,1,""],drawbox:[0,1,1,""],drawtext:[0,1,1,""],filter_:[0,1,1,""],get_args:[0,1,1,""],hflip:[0,1,1,""],hue:[0,1,1,""],input:[0,1,1,""],merge_outputs:[0,1,1,""],output:[0,1,1,""],overlay:[0,1,1,""],overwrite_output:[0,1,1,""],run:[0,1,1,""],setpts:[0,1,1,""],trim:[0,1,1,""],vflip:[0,1,1,""],view:[0,1,1,""],zoompan:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:function"},terms:{"case":0,"default":0,"function":0,"return":0,"true":0,For:0,PTS:0,The:0,These:0,Used:0,accept:0,action:0,activ:0,adjust:0,after:0,alia:0,all:0,allow:0,alpha:0,also:0,alwai:0,angl:0,anoth:0,appli:0,arab:0,area:0,arg:0,argument:0,around:0,ascent:0,asetpt:0,ask:0,aspect:0,atom:0,attempt:0,audio:0,author:0,automat:0,avoid:0,axi:0,background:0,baselin:0,basetim:0,befor:0,behavior:0,behind:0,below:0,between:0,black:0,blend:0,border:0,bordercolor:0,borderw:0,both:0,box:0,boxborderw:0,boxcolor:0,bright:0,call:0,can:0,chang:0,channel:0,charact:0,check:0,chroma:0,clip:0,cmd:0,codec:0,color:0,colorchannelmix:0,com:0,combin:0,command:0,common:0,compil:0,concat:0,concaten:0,configur:0,confus:0,constant:0,construct:0,consult:0,contain:0,continu:0,convert:0,coord:0,coordin:0,corner:0,correctli:0,correspond:0,count:0,crop:0,crop_bitmap:0,custom:0,dar:0,degre:0,deprec:0,descent:0,detail:0,dictionari:0,differ:0,directli:0,disabl:0,displai:0,distanc:0,document:0,draw:0,drawbox:0,drawn:0,drawtext:0,drop:0,due:0,durat:0,dure:0,dynam:0,each:0,edg:0,effect:0,either:0,empti:0,emul:0,enabl:0,encod:0,encount:0,end:0,end_fram:0,end_pt:0,endal:0,eof:0,eof_act:0,equival:0,error:0,escape_text:0,eval:0,evalu:0,even:0,exactli:0,exampl:0,except:0,expand:0,expans:0,explicitli:0,expr:0,express:0,fail:0,fallback:0,fals:0,famili:0,file:0,filenam:0,filter:0,filter_:0,filter_nam:0,first:0,fix:0,fix_bound:0,flag:0,flip:0,fmpeg:0,follow:0,font:0,fontcolor:0,fontcolor_expr:0,fontconfig:0,fontfil:0,fontsiz:0,forc:0,force_autohint:0,format:0,fps:0,frame:0,frame_num:0,from:0,ft_load_:0,ft_load_flag:0,gbrp:0,gener:0,get:0,get_arg:0,github:0,given:0,glyph:0,graph:0,greater:0,grid:0,handl:0,has:0,have:0,hd720:0,height:0,heigth:0,hflip:0,higher:0,highest:0,horizont:0,hour:0,how:0,hsub:0,http:0,hue:0,huge:0,ignore_global_advance_width:0,ignore_transform:0,imag:0,immedi:0,implement:0,includ:0,incom:0,index:0,inform:0,init:0,initi:0,input:0,instead:0,interpret:0,invalid:0,invert:0,its:0,join:0,just:0,kept:0,keyword:0,kkroen:0,kwarg:0,label:0,last:0,layout:0,left:0,level:0,libfontconfig:0,libfreetyp:0,libfribidi:0,librari:0,line:0,line_h:0,line_spac:0,linear_design:0,list:0,load:0,longest:0,lowest:0,luma:0,mai:0,main:0,main_h:0,main_parent_nod:0,main_w:0,mandatori:0,mani:0,manual:0,map:0,max:0,max_glyph_a:0,max_glyph_d:0,max_glyph_h:0,max_glyph_w:0,maximum:0,mean:0,merge_output:0,microsecond:0,min:0,miss:0,mix:0,mode:0,modifi:0,modul:0,monochrom:0,more:0,mp4:0,must:0,name:0,nan:0,necessari:0,need:0,neg:0,no_autohint:0,no_bitmap:0,no_hint:0,no_recurs:0,no_scal:0,node:0,none:0,normal:0,number:0,obtain:0,offici:0,offset:0,onc:0,one:0,onli:0,option:0,order:0,orient:0,other:0,otherwis:0,out:0,outlin:0,output:0,over:0,overlai:0,overlaid:0,overlay_parent_nod:0,overrid:0,overwrit:0,overwrite_output:0,pack:0,pad:0,page:0,pan:0,paramet:0,partial:0,pass:0,path:0,pedant:0,pixel:0,place:0,planar:0,pleas:0,point:0,posit:0,preced:0,present:0,process:0,pts:0,radian:0,rand:0,random:0,rang:0,rate:0,ratio:0,read:0,reason:0,refer:0,rel:0,relat:0,reload:0,render:0,repeat:0,repeatlast:0,represent:0,resolut:0,respect:0,result:0,revers:0,rgb:0,right:0,run:0,same:0,sampl:0,san:0,sar:0,satur:0,search:0,second:0,secondari:0,section:0,see:0,segment:0,select:0,sequenc:0,set:0,setpt:0,shadow:0,shadowcolor:0,shadowi:0,shadowx:0,shape:0,shorter:0,shortest:0,should:0,silenc:0,singl:0,size:0,sloppi:0,space:0,special:0,specifi:0,standard:0,start:0,start_fram:0,start_numb:0,start_pt:0,stream:0,stream_spec:0,strftime:0,string:0,subpart:0,subsampl:0,suffix:0,suppli:0,support:0,sure:0,synchron:0,synopsi:0,syntax:0,system:0,tab:0,tabsiz:0,take:0,tc24hmax:0,termin:0,text:0,text_h:0,text_shap:0,text_w:0,textfil:0,than:0,them:0,thi:0,thick:0,through:0,thrown:0,time:0,timebas:0,timecod:0,timecode_r:0,timestamp:0,togeth:0,top:0,track:0,trim:0,type:0,unit:0,unknown:0,unsaf:0,until:0,updat:0,upper:0,upward:0,url:0,use:0,used:0,user:0,using:0,utf:0,util:0,valu:0,variabl:0,variou:0,verbatim:0,vertic:0,vertical_layout:0,vflip:0,video:0,view:0,visibl:0,vsub:0,wai:0,well:0,when:0,where:0,which:0,white:0,width:0,within:0,without:0,work:0,wrap:0,write:0,you:0,yuv420:0,yuv422:0,yuv422p:0,yuv444:0,zoom:0,zoompan:0},titles:["ffmpeg-python: Python bindings for FFmpeg"],titleterms:{bind:0,ffmpeg:0,indic:0,python:0,tabl:0}}) \ No newline at end of file +Search.setIndex({docnames:["index"],envversion:52,filenames:["index.rst"],objects:{"":{ffmpeg:[0,0,0,"-"]},ffmpeg:{Error:[0,1,1,""],colorchannelmixer:[0,2,1,""],compile:[0,2,1,""],concat:[0,2,1,""],crop:[0,2,1,""],drawbox:[0,2,1,""],drawtext:[0,2,1,""],filter_:[0,2,1,""],filter_multi_output:[0,2,1,""],get_args:[0,2,1,""],hflip:[0,2,1,""],hue:[0,2,1,""],input:[0,2,1,""],merge_outputs:[0,2,1,""],output:[0,2,1,""],overlay:[0,2,1,""],overwrite_output:[0,2,1,""],probe:[0,2,1,""],run:[0,2,1,""],setpts:[0,2,1,""],trim:[0,2,1,""],vflip:[0,2,1,""],view:[0,2,1,""],zoompan:[0,2,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","exception","Python exception"],"2":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:exception","2":"py:function"},terms:{"case":0,"default":0,"function":0,"return":0,"true":0,For:0,PTS:0,The:0,These:0,Used:0,accept:0,access:0,acodec:0,action:0,activ:0,adjust:0,after:0,alia:0,all:0,allow:0,alpha:0,also:0,alwai:0,angl:0,ani:0,anoth:0,appli:0,arab:0,area:0,arg:0,argument:0,around:0,ascent:0,asetpt:0,ask:0,aspect:0,atom:0,attempt:0,audio:0,author:0,automat:0,avoid:0,axi:0,background:0,base:0,baselin:0,basetim:0,befor:0,behavior:0,behind:0,below:0,between:0,black:0,blend:0,border:0,bordercolor:0,borderw:0,both:0,box:0,boxborderw:0,boxcolor:0,bracket:0,bright:0,build:0,call:0,can:0,captur:0,capture_stderr:0,capture_stdout:0,chang:0,channel:0,charact:0,check:0,chroma:0,clip:0,cmd:0,code:0,codec:0,color:0,colorchannelmix:0,com:0,combin:0,command:0,commnad:0,common:0,compil:0,concat:0,concaten:0,configur:0,confus:0,constant:0,construct:0,consult:0,contain:0,continu:0,convert:0,coord:0,coordin:0,corner:0,correctli:0,correspond:0,count:0,crop:0,crop_bitmap:0,custom:0,dar:0,data:0,debug:0,degre:0,deprec:0,descent:0,detail:0,dictionari:0,differ:0,directli:0,disabl:0,displai:0,distanc:0,document:0,draw:0,drawbox:0,drawn:0,drawtext:0,drop:0,due:0,durat:0,dure:0,dynam:0,each:0,edg:0,effect:0,either:0,empti:0,emul:0,enabl:0,encod:0,encount:0,end:0,end_fram:0,end_pt:0,endal:0,eof:0,eof_act:0,equival:0,err:0,error:0,escape_text:0,etc:0,eval:0,evalu:0,even:0,exactli:0,exampl:0,except:0,exit:0,expand:0,expans:0,explicitli:0,expr:0,express:0,fail:0,fallback:0,fals:0,famili:0,ffmpeg_arg:0,ffprobe:0,file:0,filenam:0,filter:0,filter_:0,filter_multi_output:0,filter_nam:0,first:0,fix:0,fix_bound:0,flag:0,flip:0,fmpeg:0,follow:0,font:0,fontcolor:0,fontcolor_expr:0,fontconfig:0,fontfil:0,fontsiz:0,forc:0,force_autohint:0,format:0,fps:0,frame:0,frame_num:0,from:0,ft_load_:0,ft_load_flag:0,gbrp:0,gener:0,get_arg:0,github:0,given:0,glyph:0,graph:0,greater:0,grid:0,handl:0,has:0,have:0,hd720:0,height:0,heigth:0,hflip:0,higher:0,highest:0,horizont:0,hour:0,how:0,hsub:0,http:0,hue:0,huge:0,ignore_global_advance_width:0,ignore_transform:0,imag:0,immedi:0,implement:0,includ:0,incom:0,index:0,inform:0,init:0,initi:0,input:0,instead:0,interpret:0,invalid:0,invert:0,invok:0,its:0,ivok:0,join:0,json:0,just:0,kept:0,keyword:0,kkroen:0,kwarg:0,label:0,last:0,layout:0,left:0,level:0,libfontconfig:0,libfreetyp:0,libfribidi:0,librari:0,line:0,line_h:0,line_spac:0,linear_design:0,list:0,load:0,longest:0,lowest:0,luma:0,mai:0,main:0,main_h:0,main_parent_nod:0,main_w:0,mandatori:0,mani:0,manual:0,map:0,max:0,max_glyph_a:0,max_glyph_d:0,max_glyph_h:0,max_glyph_w:0,maximum:0,mean:0,merge_output:0,messag:0,microsecond:0,min:0,miss:0,mix:0,mode:0,modifi:0,modul:0,monochrom:0,more:0,most:0,mp4:0,multipl:0,must:0,name:0,nan:0,necessari:0,need:0,neg:0,no_autohint:0,no_bitmap:0,no_hint:0,no_recurs:0,no_scal:0,node:0,non:0,none:0,normal:0,number:0,obtain:0,offici:0,offset:0,onc:0,one:0,onli:0,oper:0,option:0,order:0,orient:0,other:0,otherwis:0,out:0,outlin:0,output:0,over:0,overlai:0,overlaid:0,overlay_parent_nod:0,overrid:0,overwrit:0,overwrite_output:0,pack:0,pad:0,page:0,pan:0,paramet:0,partial:0,pass:0,path:0,pcm:0,pedant:0,pipe:0,pixel:0,place:0,planar:0,pleas:0,point:0,posit:0,preced:0,present:0,probe:0,process:0,produc:0,properti:0,provid:0,pts:0,quiet:0,radian:0,rais:0,rand:0,random:0,rang:0,rate:0,ratio:0,read:0,reason:0,refer:0,rel:0,relat:0,reload:0,render:0,repeat:0,repeatlast:0,represent:0,resolut:0,respect:0,result:0,retriev:0,revers:0,rgb:0,right:0,run:0,same:0,sampl:0,san:0,sar:0,satur:0,search:0,second:0,secondari:0,section:0,see:0,segment:0,select:0,sent:0,sequenc:0,set:0,setpt:0,shadow:0,shadowcolor:0,shadowi:0,shadowx:0,shape:0,shorter:0,shortest:0,shorthand:0,should:0,silenc:0,singl:0,size:0,sloppi:0,space:0,special:0,specifi:0,split0:0,split1:0,split:0,standard:0,start:0,start_fram:0,start_numb:0,start_pt:0,stderr:0,stdin:0,stdout:0,stream1:0,stream2:0,stream3:0,stream:0,stream_spec:0,streams_and_filenam:0,strftime:0,string:0,subpart:0,subsampl:0,suffix:0,suppli:0,support:0,sure:0,synchron:0,synopsi:0,syntax:0,system:0,tab:0,tabsiz:0,take:0,tc24hmax:0,tell:0,termin:0,text:0,text_h:0,text_shap:0,text_w:0,textfil:0,than:0,thei:0,them:0,thi:0,thick:0,through:0,thrown:0,time:0,timebas:0,timecod:0,timecode_r:0,timestamp:0,togeth:0,top:0,track:0,trim:0,tupl:0,type:0,unit:0,unknown:0,unsaf:0,until:0,updat:0,upper:0,upward:0,url:0,use:0,used:0,useful:0,user:0,uses:0,using:0,utf:0,util:0,valu:0,variabl:0,variou:0,verbatim:0,vertic:0,vertical_layout:0,vflip:0,video:0,view:0,visibl:0,vsub:0,wai:0,well:0,whatev:0,when:0,where:0,which:0,white:0,width:0,within:0,without:0,work:0,wrap:0,write:0,you:0,yuv420:0,yuv422:0,yuv422p:0,yuv444:0,zero:0,zoom:0,zoompan:0},titles:["ffmpeg-python: Python bindings for FFmpeg"],titleterms:{bind:0,ffmpeg:0,indic:0,python:0,tabl:0}}) \ No newline at end of file diff --git a/ffmpeg/_ffmpeg.py b/ffmpeg/_ffmpeg.py index 7ea3be2..7928930 100644 --- a/ffmpeg/_ffmpeg.py +++ b/ffmpeg/_ffmpeg.py @@ -16,6 +16,11 @@ from .nodes import ( def input(filename, **kwargs): """Input file URL (ffmpeg ``-i`` option) + Any supplied kwargs are passed to ffmpeg verbatim (e.g. ``t=20``, + ``f='mp4'``, ``acodec='pcm'``, etc.). + + To tell ffmpeg to read from stdin, use ``pipe:`` as the filename. + Official documentation: `Main options <https://ffmpeg.org/ffmpeg.html#Main-options>`__ """ kwargs['filename'] = filename @@ -57,7 +62,13 @@ def output(*streams_and_filename, **kwargs): Syntax: `ffmpeg.output(stream1[, stream2, stream3...], filename, **ffmpeg_args)` - If multiple streams are provided, they are mapped to the same output. + If multiple streams are provided, they are mapped to the same + output. + + Any supplied kwargs are passed to ffmpeg verbatim (e.g. ``t=20``, + ``f='mp4'``, ``acodec='pcm'``, etc.). + + To tell ffmpeg to write to stdout, use ``pipe:`` as the filename. Official documentation: `Synopsis <https://ffmpeg.org/ffmpeg.html#Synopsis>`__ """ diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py old mode 100755 new mode 100644 index ea3a52c..a6a8c8b --- a/ffmpeg/_probe.py +++ b/ffmpeg/_probe.py @@ -1,29 +1,25 @@ import json import subprocess - - -class ProbeException(Exception): - def __init__(self, stderr_output): - super(ProbeException, self).__init__('ffprobe error') - self.stderr_output = stderr_output +from ._run import Error def probe(filename): """Run ffprobe on the specified file and return a JSON representation of the output. Raises: - ProbeException: if ffprobe returns a non-zero exit code, a ``ProbeException`` is returned with a generic error - message. The stderr output can be retrieved by accessing the ``stderr_output`` property of the exception. + :class:`ffmpeg.Error`: if ffprobe returns a non-zero exit code, + an :class:`Error` is returned with a generic error message. + The stderr output can be retrieved by accessing the + ``stderr`` property of the exception. """ args = ['ffprobe', '-show_format', '-show_streams', '-of', 'json', filename] p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode != 0: - raise ProbeException(err) + raise Error(err) return json.loads(out.decode('utf-8')) __all__ = [ 'probe', - 'ProbeException', ] diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index cec8bfb..46ec2e1 100644 --- a/ffmpeg/_run.py +++ b/ffmpeg/_run.py @@ -1,13 +1,13 @@ from __future__ import unicode_literals -from builtins import str -from past.builtins import basestring from .dag import get_outgoing_edges, topo_sort -from functools import reduce from ._utils import basestring +from builtins import str +from functools import reduce +from past.builtins import basestring import copy import operator -import subprocess as _subprocess +import subprocess from ._ffmpeg import ( input, @@ -23,6 +23,14 @@ from .nodes import ( ) +class Error(Exception): + def __init__(self, stream_spec, stdout, stderr): + super(Error, self).__init__('ffmpeg error (see stderr output for detail)') + self.stream_spec = stream_spec + self.stdout = stdout + self.stderr = stderr + + def _convert_kwargs_to_cmd_line_args(kwargs): args = [] for k in sorted(kwargs.keys()): @@ -80,8 +88,9 @@ def _allocate_filter_stream_names(filter_nodes, outgoing_edge_maps, stream_name_ for upstream_label, downstreams in list(outgoing_edge_map.items()): if len(downstreams) > 1: # TODO: automatically insert `splits` ahead of time via graph transformation. - raise ValueError('Encountered {} with multiple outgoing edges with same upstream label {!r}; a ' - '`split` filter is probably required'.format(upstream_node, upstream_label)) + raise ValueError( + 'Encountered {} with multiple outgoing edges with same upstream label {!r}; a ' + '`split` filter is probably required'.format(upstream_node, upstream_label)) stream_name_map[upstream_node, upstream_label] = 's{}'.format(stream_count) stream_count += 1 @@ -122,7 +131,7 @@ def _get_output_args(node, stream_name_map): @output_operator() def get_args(stream_spec, overwrite_output=False): - """Get command-line arguments for ffmpeg.""" + """Build command-line arguments to be passed to ffmpeg.""" nodes = get_stream_spec_nodes(stream_spec) args = [] # TODO: group nodes together, e.g. `-i somefile -r somerate`. @@ -144,27 +153,57 @@ def get_args(stream_spec, overwrite_output=False): @output_operator() -def compile(stream_spec, cmd='ffmpeg', **kwargs): - """Build command-line for ffmpeg.""" +def compile(stream_spec, cmd='ffmpeg', overwrite_output=False): + """Build command-line for invoking ffmpeg. + + The :meth:`run` function uses this to build the commnad line + arguments and should work in most cases, but calling this function + directly is useful for debugging or if you need to invoke ffmpeg + manually for whatever reason. + + This is the same as calling :meth:`get_args` except that it also + includes the ``ffmpeg`` command as the first argument. + """ if isinstance(cmd, basestring): cmd = [cmd] elif type(cmd) != list: cmd = list(cmd) - return cmd + get_args(stream_spec, **kwargs) + return cmd + get_args(stream_spec, overwrite_output=overwrite_output) @output_operator() -def run(stream_spec, cmd='ffmpeg', **kwargs): - """Run ffmpeg on node graph. +def run( + stream_spec, cmd='ffmpeg', capture_stdout=False, capture_stderr=False, input=None, + quiet=False, overwrite_output=False): + """Ivoke ffmpeg for the supplied node graph. Args: - **kwargs: keyword-arguments passed to ``get_args()`` (e.g. ``overwrite_output=True``). + capture_stdout: if True, capture stdout (to be used with + ``pipe:`` ffmpeg outputs). + capture_stderr: if True, capture stderr. + quiet: shorthand for setting ``capture_stdout`` and ``capture_stderr``. + input: text to be sent to stdin (to be used with ``pipe:`` + ffmpeg inputs) + **kwargs: keyword-arguments passed to ``get_args()`` (e.g. + ``overwrite_output=True``). + + Returns: (out, err) tuple containing captured stdout and stderr data. """ - _subprocess.check_call(compile(stream_spec, cmd, **kwargs)) + args = compile(stream_spec, cmd, overwrite_output=overwrite_output) + stdin_stream = subprocess.PIPE if input else None + stdout_stream = subprocess.PIPE if capture_stdout or quiet else None + stderr_stream = subprocess.PIPE if capture_stderr or quiet else None + p = subprocess.Popen(args, stdin=stdin_stream, stdout=stdout_stream, stderr=stderr_stream) + out, err = p.communicate(input) + retcode = p.poll() + if retcode: + raise Error(stream_spec, out, err) + return out, err __all__ = [ 'compile', + 'Error', 'get_args', 'run', ] diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index c7805fa..2a322b4 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -111,6 +111,10 @@ def test_global_args(): assert out_file.get_args() == ['-i', 'dummy.mp4', 'dummy2.mp4', '-progress', 'someurl'] +def _get_simple_example(): + return ffmpeg.input(TEST_INPUT_FILE1).output(TEST_OUTPUT_FILE1) + + def _get_complex_filter_example(): split = (ffmpeg .input(TEST_INPUT_FILE1) @@ -313,33 +317,72 @@ def test_compile(): def test_run(): stream = _get_complex_filter_example() - ffmpeg.run(stream) + out, err = ffmpeg.run(stream) + assert out is None + assert err is None -def test_run_multi_output(): +@pytest.mark.parametrize('capture_stdout', [True, False]) +@pytest.mark.parametrize('capture_stderr', [True, False]) +def test_run__capture_out(mocker, capture_stdout, capture_stderr): + mocker.patch.object(ffmpeg._run, 'compile', return_value=['echo', 'test']) + stream = _get_simple_example() + out, err = ffmpeg.run(stream, capture_stdout=capture_stdout, capture_stderr=capture_stderr) + if capture_stdout: + assert out == 'test\n' + else: + assert out is None + if capture_stderr: + assert err == '' + else: + assert err is None + + +def test_run__input_output(mocker): + mocker.patch.object(ffmpeg._run, 'compile', return_value=['cat']) + stream = _get_simple_example() + out, err = ffmpeg.run(stream, input='test', capture_stdout=True) + assert out == 'test' + assert err is None + + +@pytest.mark.parametrize('capture_stdout', [True, False]) +@pytest.mark.parametrize('capture_stderr', [True, False]) +def test_run__error(mocker, capture_stdout, capture_stderr): + mocker.patch.object(ffmpeg._run, 'compile', return_value=['ffmpeg']) + stream = _get_complex_filter_example() + with pytest.raises(ffmpeg.Error) as excinfo: + out, err = ffmpeg.run(stream, capture_stdout=capture_stdout, capture_stderr=capture_stderr) + out = excinfo.value.stdout + err = excinfo.value.stderr + if capture_stdout: + assert out == '' + else: + assert out is None + if capture_stderr: + assert err.startswith('ffmpeg version') + else: + assert err is None + + +def test_run__multi_output(): in_ = ffmpeg.input(TEST_INPUT_FILE1) out1 = in_.output(TEST_OUTPUT_FILE1) out2 = in_.output(TEST_OUTPUT_FILE2) ffmpeg.run([out1, out2], overwrite_output=True) -def test_run_dummy_cmd(): +def test_run__dummy_cmd(): stream = _get_complex_filter_example() ffmpeg.run(stream, cmd='true') -def test_run_dummy_cmd_list(): +def test_run__dummy_cmd_list(): stream = _get_complex_filter_example() ffmpeg.run(stream, cmd=['true', 'ignored']) -def test_run_failing_cmd(): - stream = _get_complex_filter_example() - with pytest.raises(subprocess.CalledProcessError): - ffmpeg.run(stream, cmd='false') - - -def test_custom_filter(): +def test_filter__custom(): stream = ffmpeg.input('dummy.mp4') stream = ffmpeg.filter_(stream, 'custom_filter', 'a', 'b', kwarg1='c') stream = ffmpeg.output(stream, 'dummy2.mp4') @@ -351,7 +394,7 @@ def test_custom_filter(): ] -def test_custom_filter_fluent(): +def test_filter__custom_fluent(): stream = (ffmpeg .input('dummy.mp4') .filter_('custom_filter', 'a', 'b', kwarg1='c') diff --git a/requirements-base.txt b/requirements-base.txt index b5e8ae3..6afd152 100644 --- a/requirements-base.txt +++ b/requirements-base.txt @@ -1,5 +1,6 @@ future pytest +pytest-mock pytest-runner sphinx tox diff --git a/requirements.txt b/requirements.txt index 84710e2..dfec4a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,26 @@ alabaster==0.7.10 +apipkg==1.4 Babel==2.5.1 certifi==2017.7.27.1 chardet==3.0.4 docutils==0.14 +execnet==1.5.0 +funcsigs==1.0.2 future==0.16.0 idna==2.6 imagesize==0.7.1 Jinja2==2.9.6 MarkupSafe==1.0 +mock==2.0.0 +pbr==4.0.3 pluggy==0.5.2 py==1.4.34 Pygments==2.2.0 pytest==3.2.3 +pytest-forked==0.2 +pytest-mock==1.10.0 pytest-runner==3.0 +pytest-xdist==1.22.2 pytz==2017.3 requests==2.18.4 six==1.11.0 diff --git a/setup.py b/setup.py index 6ee7bb0..3e4ee38 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ setup( name='ffmpeg-python', packages=['ffmpeg'], setup_requires=['pytest-runner'], - tests_require=['pytest'], + tests_require=['pytest', 'pytest-mock'], version=version, description='Python bindings for FFmpeg - with support for complex filtering', author='Karl Kroening', From 4558c25ceddfb365fa9a5a2aceb25bd63e67118b Mon Sep 17 00:00:00 2001 From: Karl Kroening <karlk@kralnet.us> Date: Sun, 20 May 2018 01:17:54 -0700 Subject: [PATCH 2/5] Update tox.ini --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index c187ba3..f86ec4b 100644 --- a/tox.ini +++ b/tox.ini @@ -11,3 +11,4 @@ commands = py.test -vv deps = future pytest + pytest-mock From 940b05f3fcf46f60eb02ebdabb58289db033dfae Mon Sep 17 00:00:00 2001 From: Karl Kroening <karlk@kralnet.us> Date: Sun, 20 May 2018 01:21:30 -0700 Subject: [PATCH 3/5] Fix ffprobe exception test --- ffmpeg/tests/test_ffmpeg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 2a322b4..7ab48a5 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -491,7 +491,7 @@ def test_ffprobe(): def test_ffprobe_exception(): - with pytest.raises(ffmpeg.ProbeException) as excinfo: + with pytest.raises(ffmpeg.Error) as excinfo: ffmpeg.probe(BOGUS_INPUT_FILE) assert str(excinfo.value) == 'ffprobe error' - assert b'No such file or directory' in excinfo.value.stderr_output + assert 'No such file or directory'.encode() in excinfo.value.stderr From 9a487e8603bf881519cd04afe34c1272adfa7d15 Mon Sep 17 00:00:00 2001 From: Karl Kroening <karlk@kralnet.us> Date: Sun, 20 May 2018 01:29:59 -0700 Subject: [PATCH 4/5] Fix exception params --- ffmpeg/_probe.py | 2 +- ffmpeg/_run.py | 7 +++---- ffmpeg/tests/test_ffmpeg.py | 33 +++++++++++++++++---------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py index a6a8c8b..d3658fd 100644 --- a/ffmpeg/_probe.py +++ b/ffmpeg/_probe.py @@ -16,7 +16,7 @@ def probe(filename): p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode != 0: - raise Error(err) + raise Error('ffprobe', out, err) return json.loads(out.decode('utf-8')) diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index 46ec2e1..c5a2b52 100644 --- a/ffmpeg/_run.py +++ b/ffmpeg/_run.py @@ -24,9 +24,8 @@ from .nodes import ( class Error(Exception): - def __init__(self, stream_spec, stdout, stderr): - super(Error, self).__init__('ffmpeg error (see stderr output for detail)') - self.stream_spec = stream_spec + def __init__(self, cmd, stdout, stderr): + super(Error, self).__init__('{} error (see stderr output for detail)'.format(cmd)) self.stdout = stdout self.stderr = stderr @@ -197,7 +196,7 @@ def run( out, err = p.communicate(input) retcode = p.poll() if retcode: - raise Error(stream_spec, out, err) + raise Error('ffmpeg', out, err) return out, err diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 7ab48a5..06d00c7 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -101,7 +101,7 @@ def test_stream_repr(): assert repr(dummy_out) == 'dummy()[{!r}] <{}>'.format(dummy_out.label, dummy_out.node.short_hash) -def test_get_args_simple(): +def test__get_args__simple(): out_file = ffmpeg.input('dummy.mp4').output('dummy2.mp4') assert out_file.get_args() == ['-i', 'dummy.mp4', 'dummy2.mp4'] @@ -138,7 +138,7 @@ def _get_complex_filter_example(): ) -def test_get_args_complex_filter(): +def test__get_args__complex_filter(): out = _get_complex_filter_example() args = ffmpeg.get_args(out) assert args == ['-i', TEST_INPUT_FILE1, @@ -309,13 +309,13 @@ def test_filter_text_arg_str_escape(): # subprocess.check_call(['ffmpeg', '-version']) -def test_compile(): +def test__compile(): out_file = ffmpeg.input('dummy.mp4').output('dummy2.mp4') assert out_file.compile() == ['ffmpeg', '-i', 'dummy.mp4', 'dummy2.mp4'] assert out_file.compile(cmd='ffmpeg.old') == ['ffmpeg.old', '-i', 'dummy.mp4', 'dummy2.mp4'] -def test_run(): +def test__run(): stream = _get_complex_filter_example() out, err = ffmpeg.run(stream) assert out is None @@ -324,7 +324,7 @@ def test_run(): @pytest.mark.parametrize('capture_stdout', [True, False]) @pytest.mark.parametrize('capture_stderr', [True, False]) -def test_run__capture_out(mocker, capture_stdout, capture_stderr): +def test__run__capture_out(mocker, capture_stdout, capture_stderr): mocker.patch.object(ffmpeg._run, 'compile', return_value=['echo', 'test']) stream = _get_simple_example() out, err = ffmpeg.run(stream, capture_stdout=capture_stdout, capture_stderr=capture_stderr) @@ -338,7 +338,7 @@ def test_run__capture_out(mocker, capture_stdout, capture_stderr): assert err is None -def test_run__input_output(mocker): +def test__run__input_output(mocker): mocker.patch.object(ffmpeg._run, 'compile', return_value=['cat']) stream = _get_simple_example() out, err = ffmpeg.run(stream, input='test', capture_stdout=True) @@ -348,11 +348,12 @@ def test_run__input_output(mocker): @pytest.mark.parametrize('capture_stdout', [True, False]) @pytest.mark.parametrize('capture_stderr', [True, False]) -def test_run__error(mocker, capture_stdout, capture_stderr): +def test__run__error(mocker, capture_stdout, capture_stderr): mocker.patch.object(ffmpeg._run, 'compile', return_value=['ffmpeg']) stream = _get_complex_filter_example() with pytest.raises(ffmpeg.Error) as excinfo: out, err = ffmpeg.run(stream, capture_stdout=capture_stdout, capture_stderr=capture_stderr) + assert str(excinfo.value) == 'ffmpeg error (see stderr output for detail)' out = excinfo.value.stdout err = excinfo.value.stderr if capture_stdout: @@ -365,24 +366,24 @@ def test_run__error(mocker, capture_stdout, capture_stderr): assert err is None -def test_run__multi_output(): +def test__run__multi_output(): in_ = ffmpeg.input(TEST_INPUT_FILE1) out1 = in_.output(TEST_OUTPUT_FILE1) out2 = in_.output(TEST_OUTPUT_FILE2) ffmpeg.run([out1, out2], overwrite_output=True) -def test_run__dummy_cmd(): +def test__run__dummy_cmd(): stream = _get_complex_filter_example() ffmpeg.run(stream, cmd='true') -def test_run__dummy_cmd_list(): +def test__run__dummy_cmd_list(): stream = _get_complex_filter_example() ffmpeg.run(stream, cmd=['true', 'ignored']) -def test_filter__custom(): +def test__filter__custom(): stream = ffmpeg.input('dummy.mp4') stream = ffmpeg.filter_(stream, 'custom_filter', 'a', 'b', kwarg1='c') stream = ffmpeg.output(stream, 'dummy2.mp4') @@ -394,7 +395,7 @@ def test_filter__custom(): ] -def test_filter__custom_fluent(): +def test__filter__custom_fluent(): stream = (ffmpeg .input('dummy.mp4') .filter_('custom_filter', 'a', 'b', kwarg1='c') @@ -408,7 +409,7 @@ def test_filter__custom_fluent(): ] -def test_merge_outputs(): +def test__merge_outputs(): in_ = ffmpeg.input('in.mp4') out1 = in_.output('out1.mp4') out2 = in_.output('out2.mp4') @@ -484,14 +485,14 @@ def test_pipe(): assert out_data == in_data[start_frame*frame_size:] -def test_ffprobe(): +def test__probe(): data = ffmpeg.probe(TEST_INPUT_FILE1) assert set(data.keys()) == {'format', 'streams'} assert data['format']['duration'] == '7.036000' -def test_ffprobe_exception(): +def test__probe__exception(): with pytest.raises(ffmpeg.Error) as excinfo: ffmpeg.probe(BOGUS_INPUT_FILE) - assert str(excinfo.value) == 'ffprobe error' + assert str(excinfo.value) == 'ffprobe error (see stderr output for detail)' assert 'No such file or directory'.encode() in excinfo.value.stderr From 6a2d3381b79d5a8feca2c11733d0e80388c6e28e Mon Sep 17 00:00:00 2001 From: Karl Kroening <karlk@kralnet.us> Date: Sun, 20 May 2018 01:36:46 -0700 Subject: [PATCH 5/5] Fix tests --- ffmpeg/tests/test_ffmpeg.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 06d00c7..25aedd1 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -329,11 +329,11 @@ def test__run__capture_out(mocker, capture_stdout, capture_stderr): stream = _get_simple_example() out, err = ffmpeg.run(stream, capture_stdout=capture_stdout, capture_stderr=capture_stderr) if capture_stdout: - assert out == 'test\n' + assert out == 'test\n'.encode() else: assert out is None if capture_stderr: - assert err == '' + assert err == ''.encode() else: assert err is None @@ -341,8 +341,8 @@ def test__run__capture_out(mocker, capture_stdout, capture_stderr): def test__run__input_output(mocker): mocker.patch.object(ffmpeg._run, 'compile', return_value=['cat']) stream = _get_simple_example() - out, err = ffmpeg.run(stream, input='test', capture_stdout=True) - assert out == 'test' + out, err = ffmpeg.run(stream, input='test'.encode(), capture_stdout=True) + assert out == 'test'.encode() assert err is None @@ -357,11 +357,11 @@ def test__run__error(mocker, capture_stdout, capture_stderr): out = excinfo.value.stdout err = excinfo.value.stderr if capture_stdout: - assert out == '' + assert out == ''.encode() else: assert out is None if capture_stderr: - assert err.startswith('ffmpeg version') + assert err.decode().startswith('ffmpeg version') else: assert err is None