# HG changeset patch # User Yading Song # Date 1366535783 -7200 # Node ID f445c3017523a4e2ea41235b8b47e64fdaceb56a # Parent 6840f77b83aac4b9e1185e80843b7ddbf664579f new files diff -r 6840f77b83aa -r f445c3017523 css/bootstrap-responsive.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/bootstrap-responsive.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1088 @@ +/*! + * Bootstrap Responsive v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} + +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .dropdown-menu a:hover { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:hover { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff -r 6840f77b83aa -r f445c3017523 css/bootstrap-responsive.min.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/bootstrap-responsive.min.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff -r 6840f77b83aa -r f445c3017523 css/bootstrap.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/bootstrap.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,5893 @@ +/*! + * Bootstrap v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + width: 940px; +} + +.span11 { + width: 860px; +} + +.span10 { + width: 780px; +} + +.span9 { + width: 700px; +} + +.span8 { + width: 620px; +} + +.span7 { + width: 540px; +} + +.span6 { + width: 460px; +} + +.span5 { + width: 380px; +} + +.span4 { + width: 300px; +} + +.span3 { + width: 220px; +} + +.span2 { + width: 140px; +} + +.span1 { + width: 60px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} + +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} + +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} + +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} + +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} + +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} + +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} + +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} + +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} + +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} + +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} + +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} + +.container:before, +.container:after { + display: table; + line-height: 0; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} + +.container-fluid:before, +.container-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} + +small { + font-size: 85%; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +cite { + font-style: normal; +} + +.muted { + color: #999999; +} + +.text-warning { + color: #c09853; +} + +a.text-warning:hover { + color: #a47e3c; +} + +.text-error { + color: #b94a48; +} + +a.text-error:hover { + color: #953b39; +} + +.text-info { + color: #3a87ad; +} + +a.text-info:hover { + color: #2d6987; +} + +.text-success { + color: #468847; +} + +a.text-success:hover { + color: #356635; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + line-height: 40px; +} + +h1 { + font-size: 38.5px; +} + +h2 { + font-size: 31.5px; +} + +h3 { + font-size: 24.5px; +} + +h4 { + font-size: 17.5px; +} + +h5 { + font-size: 14px; +} + +h6 { + font-size: 11.9px; +} + +h1 small { + font-size: 24.5px; +} + +h2 small { + font-size: 17.5px; +} + +h3 small { + font-size: 14px; +} + +h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} + +li { + line-height: 20px; +} + +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 10px; +} + +.dl-horizontal { + *zoom: 1; +} + +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + line-height: 0; + content: ""; +} + +.dl-horizontal:after { + clear: both; +} + +.dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dl-horizontal dd { + margin-left: 180px; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 25px; +} + +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} + +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + color: inherit; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; + cursor: pointer; +} + +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +select[multiple], +select[size] { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} + +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} + +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} + +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} + +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} + +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} + +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} + +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} + +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} + +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} + +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} + +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + +.control-group.warning > label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} + +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} + +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error > label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} + +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} + +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success > label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} + +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} + +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group.info > label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} + +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} + +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:required:invalid, +textarea:focus:required:invalid, +select:focus:required:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:required:invalid:focus, +textarea:focus:required:invalid:focus, +select:focus:required:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + margin-bottom: 5px; + font-size: 0; + white-space: nowrap; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn, +.input-append select + .btn-group .btn, +.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls:first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child th:first-child, +.table-bordered tbody:first-child tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child th:last-child, +.table-bordered tbody:first-child tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child th:first-child, +.table-bordered tbody:last-child tr:last-child td:first-child, +.table-bordered tfoot:last-child tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child th:last-child, +.table-bordered tbody:last-child tr:last-child td:last-child, +.table-bordered tfoot:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody tr:nth-child(odd) td, +.table-striped tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} + +.table-hover tbody tr:hover td, +.table-hover tbody tr:hover th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success td { + background-color: #dff0d8; +} + +.table tbody tr.error td { + background-color: #f2dede; +} + +.table tbody tr.warning td { + background-color: #fcf8e3; +} + +.table tbody tr.info td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover td { + background-color: #c4e3f3; +} + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +/* White icons with optional class, or on hover/active states of certain elements */ + +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu li > a:hover, +.dropdown-menu li > a:focus, +.dropdown-submenu:hover > a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .active > a, +.dropdown-menu .active > a:hover { + color: #333333; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .disabled > a, +.dropdown-menu .disabled > a:hover { + color: #999999; +} + +.dropdown-menu .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; + background-image: none; +} + +.open { + *z-index: 1000; +} + +.open > .dropdown-menu { + display: block; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu > a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +.collapse.in { + height: auto; +} + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + *line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #bbbbbb; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #a2a2a2; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + *background-color: #d9d9d9; + /* Buttons in IE7 don't get borders, so darken on hover */ + + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled] { + cursor: default; + background-color: #e6e6e6; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 2px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini { + padding: 1px 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn { + border-color: #c5c5c5; + border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover { + color: #333333; + text-decoration: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group + .btn-group { + margin-left: 5px; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar .btn + .btn, +.btn-toolbar .btn-group + .btn, +.btn-toolbar .btn + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group > .btn + .btn { + margin-left: -1px; +} + +.btn-group > .btn, +.btn-group > .dropdown-menu { + font-size: 14px; +} + +.btn-group > .btn-mini { + font-size: 11px; +} + +.btn-group > .btn-small { + font-size: 12px; +} + +.btn-group > .btn-large { + font-size: 16px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + *padding-top: 5px; + padding-right: 8px; + *padding-bottom: 5px; + padding-left: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group > .btn-mini + .dropdown-toggle { + *padding-top: 2px; + padding-right: 5px; + *padding-bottom: 2px; + padding-left: 5px; +} + +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} + +.btn-group > .btn-large + .dropdown-toggle { + *padding-top: 7px; + padding-right: 12px; + *padding-bottom: 7px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} + +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} + +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} + +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} + +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + margin-top: 8px; + margin-left: 0; +} + +.btn-mini .caret, +.btn-small .caret, +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical .btn { + display: block; + float: none; + width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + color: #c09853; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert h4 { + margin: 0; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav > li > a { + display: block; +} + +.nav > li > a:hover { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li + .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list > li > a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list > li > a { + padding: 3px 15px; +} + +.nav-list > .active > a, +.nav-list > .active > a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs > li, +.nav-pills > li { + float: left; +} + +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + margin-bottom: -1px; +} + +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover { + color: #ffffff; + background-color: #0088cc; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.nav .dropdown-toggle .caret { + margin-top: 6px; + border-top-color: #0088cc; + border-bottom-color: #0088cc; +} + +.nav .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav > .dropdown.active > a:hover { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open > a:hover { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below > .nav-tabs > li > a:hover { + border-top-color: #ddd; + border-bottom-color: transparent; +} + +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover { + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav > .disabled > a { + color: #999999; +} + +.nav > .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; + color: #777777; +} + +.navbar-inner { + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover { + text-decoration: none; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 6px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav > li { + float: left; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav > li > .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav > li > .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse { + color: #999999; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb .active { + color: #999999; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul > li { + display: inline; +} + +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul > li > a:hover, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} + +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} + +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 1px 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li > a:hover { + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 1050; + width: 560px; + margin: -250px 0 0 -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.fade { + top: -25%; + -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; + -moz-transition: opacity 0.3s linear, top 0.3s ease-out; + -o-transition: opacity 0.3s linear, top 0.3s ease-out; + transition: opacity 0.3s linear, top 0.3s ease-out; +} + +.modal.fade.in { + top: 50%; +} + +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} + +.modal-header .close { + margin-top: 2px; +} + +.modal-header h3 { + margin: 0; + line-height: 30px; +} + +.modal-body { + max-height: 400px; + padding: 15px; + overflow-y: auto; +} + +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + line-height: 0; + content: ""; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + margin-top: -3px; +} + +.tooltip.right { + margin-left: 3px; +} + +.tooltip.bottom { + margin-top: 3px; +} + +.tooltip.left { + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + width: 236px; + padding: 1px; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover-content p, +.popover-content ul, +.popover-content ol { + margin-bottom: 0; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: inline-block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow:after { + z-index: -1; + content: ""; +} + +.popover.top .arrow { + bottom: -10px; + left: 50%; + margin-left: -10px; + border-top-color: #ffffff; + border-width: 10px 10px 0; +} + +.popover.top .arrow:after { + bottom: -1px; + left: -11px; + border-top-color: rgba(0, 0, 0, 0.25); + border-width: 11px 11px 0; +} + +.popover.right .arrow { + top: 50%; + left: -10px; + margin-top: -10px; + border-right-color: #ffffff; + border-width: 10px 10px 10px 0; +} + +.popover.right .arrow:after { + bottom: -11px; + left: -1px; + border-right-color: rgba(0, 0, 0, 0.25); + border-width: 11px 11px 11px 0; +} + +.popover.bottom .arrow { + top: -10px; + left: 50%; + margin-left: -10px; + border-bottom-color: #ffffff; + border-width: 0 10px 10px; +} + +.popover.bottom .arrow:after { + top: -1px; + left: -11px; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-width: 0 11px 11px; +} + +.popover.left .arrow { + top: 50%; + right: -10px; + margin-top: -10px; + border-left-color: #ffffff; + border-width: 10px 0 10px 10px; +} + +.popover.left .arrow:after { + right: -1px; + bottom: -11px; + border-left-color: rgba(0, 0, 0, 0.25); + border-width: 11px 0 11px 11px; +} + +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before, +.thumbnails:after { + display: table; + line-height: 0; + content: ""; +} + +.thumbnails:after { + clear: both; +} + +.row-fluid .thumbnails { + margin-left: 0; +} + +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} + +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +a.thumbnail:hover { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail > img { + display: block; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #555555; +} + +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media .pull-left { + margin-right: 10px; +} + +.media .pull-right { + margin-left: 10px; +} + +.media-list { + margin-left: 0; + list-style: none; +} + +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; +} + +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.badge { + padding-right: 9px; + padding-left: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} + +a.label:hover, +a.badge:hover { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label-important, +.badge-important { + background-color: #b94a48; +} + +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} + +.label-warning, +.badge-warning { + background-color: #f89406; +} + +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} + +.label-success, +.badge-success { + background-color: #468847; +} + +.label-success[href], +.badge-success[href] { + background-color: #356635; +} + +.label-info, +.badge-info { + background-color: #3a87ad; +} + +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} + +.label-inverse, +.badge-inverse { + background-color: #333333; +} + +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} + +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} + +.btn-mini .label, +.btn-mini .badge { + top: 0; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} + +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} + +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion { + margin-bottom: 20px; +} + +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading { + border-bottom: 0; +} + +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +.accordion-toggle { + cursor: pointer; +} + +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel .item > img { + display: block; + line-height: 1; +} + +.carousel .active, +.carousel .next, +.carousel .prev { + display: block; +} + +.carousel .active { + left: 0; +} + +.carousel .next, +.carousel .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel .next { + left: 100%; +} + +.carousel .prev { + left: -100%; +} + +.carousel .next.left, +.carousel .prev.right { + left: 0; +} + +.carousel .active.left { + left: -100%; +} + +.carousel .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right { + right: 15px; + left: auto; +} + +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + color: inherit; +} + +.hero-unit li { + line-height: 30px; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff -r 6840f77b83aa -r f445c3017523 css/bootstrap.min.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/bootstrap.min.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,9 @@ +/*! + * Bootstrap v2.2.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal;cursor:pointer}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn,.input-append select+.btn-group .btn,.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#333;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:2px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini{padding:1px 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px}.btn-group>.btn-mini{font-size:11px}.btn-group>.btn-small{font-size:12px}.btn-group>.btn-large{font-size:16px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical .btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible;color:#777}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse{color:#999}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#ccc}.breadcrumb .active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:1px 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow:after{z-index:-1;content:""}.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-top-color:#fff;border-width:10px 10px 0}.popover.top .arrow:after{bottom:-1px;left:-11px;border-top-color:rgba(0,0,0,0.25);border-width:11px 11px 0}.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-right-color:#fff;border-width:10px 10px 10px 0}.popover.right .arrow:after{bottom:-11px;left:-1px;border-right-color:rgba(0,0,0,0.25);border-width:11px 11px 11px 0}.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-bottom-color:#fff;border-width:0 10px 10px}.popover.bottom .arrow:after{top:-1px;left:-11px;border-bottom-color:rgba(0,0,0,0.25);border-width:0 11px 11px}.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-left-color:#fff;border-width:10px 0 10px 10px}.popover.left .arrow:after{right:-1px;bottom:-11px;border-left-color:rgba(0,0,0,0.25);border-width:11px 0 11px 11px}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff -r 6840f77b83aa -r f445c3017523 css/styles.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/styles.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +.centerIcon{ +text-align: center; +} +#buttonDiv{ + margin-bottom: 20px; +} + +.one{ + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + border-color: #0081c2; + border-style: solid; + border-width: 10px; + padding-top: 20px; +} + +.two{ + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + border-color: #c67605; + border-style: solid; + border-width: 10px; + padding-top: 20px; +} diff -r 6840f77b83aa -r f445c3017523 css/ui.selectmenu.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/css/ui.selectmenu.css Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +/* Selectmenu +----------------------------------*/ +.ui-selectmenu { display: block; position:relative; height:2em; text-decoration: none; overflow:hidden;} +.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; } +.ui-selectmenu-menu { padding:0; margin:0; list-style:none; position:absolute; top: 0; visibility: hidden; overflow: auto; } +.ui-selectmenu-open { visibility: visible; } +.ui-selectmenu-menu-popup { margin-top: -1px; } +.ui-selectmenu-menu-dropdown { } +.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; } +.ui-selectmenu-menu li a,.ui-selectmenu-status {line-height: 1.4em; display:block; padding:.3em 1em; outline:none; text-decoration:none; } +.ui-selectmenu-menu li.ui-selectmenu-hasIcon a, +.ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; } +.ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; } +.ui-selectmenu-status { line-height: 1.4em; } +.ui-selectmenu-open li.ui-selectmenu-item-focus a { } +.ui-selectmenu-open li.ui-selectmenu-item-selected { } +.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; } +.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; } +.ui-selectmenu-menu li .ui-selectmenu-item-content { } +.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; } +/*for optgroups*/ +.ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; } +.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding:.6em .5em 0; font-weight: bold; } +.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; } \ No newline at end of file diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_subtitles.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_subtitles.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2011 Baptiste Coudurier + * Copyright (c) 2011 Stefano Sabatini + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Libass subtitles burning filter. + * + * @see{http://www.matroska.org/technical/specs/subtitles/ssa.html} + */ + +#include + +#include "config.h" +#if CONFIG_SUBTITLES_FILTER +# include "libavcodec/avcodec.h" +# include "libavformat/avformat.h" +#endif +#include "libavutil/avstring.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "drawutils.h" +#include "avfilter.h" +#include "internal.h" +#include "formats.h" +#include "video.h" + +typedef struct { + const AVClass *class; + ASS_Library *library; + ASS_Renderer *renderer; + ASS_Track *track; + char *filename; + char *charenc; + uint8_t rgba_map[4]; + int pix_step[4]; ///< steps per pixel for each plane of the main output + int original_w, original_h; + FFDrawContext draw; +} AssContext; + +#define OFFSET(x) offsetof(AssContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +#define COMMON_OPTIONS \ + {"filename", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ + {"f", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ + {"original_size", "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ + +/* libass supports a log level ranging from 0 to 7 */ +static const int ass_libavfilter_log_level_map[] = { + AV_LOG_QUIET, /* 0 */ + AV_LOG_PANIC, /* 1 */ + AV_LOG_FATAL, /* 2 */ + AV_LOG_ERROR, /* 3 */ + AV_LOG_WARNING, /* 4 */ + AV_LOG_INFO, /* 5 */ + AV_LOG_VERBOSE, /* 6 */ + AV_LOG_DEBUG, /* 7 */ +}; + +static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx) +{ + int level = ass_libavfilter_log_level_map[ass_level]; + + av_vlog(ctx, level, fmt, args); + av_log(ctx, level, "\n"); +} + +static av_cold int init(AVFilterContext *ctx) +{ + AssContext *ass = ctx->priv; + + if (!ass->filename) { + av_log(ctx, AV_LOG_ERROR, "No filename provided!\n"); + return AVERROR(EINVAL); + } + + ass->library = ass_library_init(); + if (!ass->library) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n"); + return AVERROR(EINVAL); + } + ass_set_message_cb(ass->library, ass_log, ctx); + + ass->renderer = ass_renderer_init(ass->library); + if (!ass->renderer) { + av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n"); + return AVERROR(EINVAL); + } + + ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1); + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AssContext *ass = ctx->priv; + + if (ass->track) + ass_free_track(ass->track); + if (ass->renderer) + ass_renderer_done(ass->renderer); + if (ass->library) + ass_library_done(ass->library); +} + +static int query_formats(AVFilterContext *ctx) +{ + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + AssContext *ass = inlink->dst->priv; + + ff_draw_init(&ass->draw, inlink->format, 0); + + ass_set_frame_size (ass->renderer, inlink->w, inlink->h); + if (ass->original_w && ass->original_h) + ass_set_aspect_ratio(ass->renderer, (double)inlink->w / inlink->h, + (double)ass->original_w / ass->original_h); + + return 0; +} + +/* libass stores an RGBA color in the format RRGGBBTT, where TT is the transparency level */ +#define AR(c) ( (c)>>24) +#define AG(c) (((c)>>16)&0xFF) +#define AB(c) (((c)>>8) &0xFF) +#define AA(c) ((0xFF-c) &0xFF) + +static void overlay_ass_image(AssContext *ass, AVFrame *picref, + const ASS_Image *image) +{ + for (; image; image = image->next) { + uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)}; + FFDrawColor color; + ff_draw_color(&ass->draw, &color, rgba_color); + ff_blend_mask(&ass->draw, &color, + picref->data, picref->linesize, + picref->width, picref->height, + image->bitmap, image->stride, image->w, image->h, + 3, 0, image->dst_x, image->dst_y); + } +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *picref) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + AssContext *ass = ctx->priv; + int detect_change = 0; + double time_ms = picref->pts * av_q2d(inlink->time_base) * 1000; + ASS_Image *image = ass_render_frame(ass->renderer, ass->track, + time_ms, &detect_change); + + if (detect_change) + av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%f\n", time_ms); + + overlay_ass_image(ass, picref, image); + + return ff_filter_frame(outlink, picref); +} + +static const AVFilterPad ass_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + .needs_writable = 1, + }, + { NULL } +}; + +static const AVFilterPad ass_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +#if CONFIG_ASS_FILTER + +static const AVOption ass_options[] = { + COMMON_OPTIONS + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(ass); + +static av_cold int init_ass(AVFilterContext *ctx) +{ + AssContext *ass = ctx->priv; + int ret = init(ctx); + + if (ret < 0) + return ret; + + ass->track = ass_read_file(ass->library, ass->filename, NULL); + if (!ass->track) { + av_log(ctx, AV_LOG_ERROR, + "Could not create a libass track when reading file '%s'\n", + ass->filename); + return AVERROR(EINVAL); + } + return 0; +} + +AVFilter avfilter_vf_ass = { + .name = "ass", + .description = NULL_IF_CONFIG_SMALL("Render ASS subtitles onto input video using the libass library."), + .priv_size = sizeof(AssContext), + .init = init_ass, + .uninit = uninit, + .query_formats = query_formats, + .inputs = ass_inputs, + .outputs = ass_outputs, + .priv_class = &ass_class, +}; +#endif + +#if CONFIG_SUBTITLES_FILTER + +static const AVOption subtitles_options[] = { + COMMON_OPTIONS + {"charenc", "set input character encoding", OFFSET(charenc), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(subtitles); + +static av_cold int init_subtitles(AVFilterContext *ctx) +{ + int ret, sid; + AVDictionary *codec_opts = NULL; + AVFormatContext *fmt = NULL; + AVCodecContext *dec_ctx = NULL; + AVCodec *dec = NULL; + const AVCodecDescriptor *dec_desc; + AVStream *st; + AVPacket pkt; + AssContext *ass = ctx->priv; + + /* Init libass */ + ret = init(ctx); + if (ret < 0) + return ret; + ass->track = ass_new_track(ass->library); + if (!ass->track) { + av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n"); + return AVERROR(EINVAL); + } + + /* Open subtitles file */ + ret = avformat_open_input(&fmt, ass->filename, NULL, NULL); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename); + goto end; + } + ret = avformat_find_stream_info(fmt, NULL); + if (ret < 0) + goto end; + + /* Locate subtitles stream */ + ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Unable to locate subtitle stream in %s\n", + ass->filename); + goto end; + } + sid = ret; + st = fmt->streams[sid]; + + /* Open decoder */ + dec_ctx = st->codec; + dec = avcodec_find_decoder(dec_ctx->codec_id); + if (!dec) { + av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n", + avcodec_get_name(dec_ctx->codec_id)); + return AVERROR(EINVAL); + } + dec_desc = avcodec_descriptor_get(dec_ctx->codec_id); + if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) { + av_log(ctx, AV_LOG_ERROR, + "Only text based subtitles are currently supported\n"); + return AVERROR_PATCHWELCOME; + } + if (ass->charenc) + av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0); + ret = avcodec_open2(dec_ctx, dec, &codec_opts); + if (ret < 0) + goto end; + + /* Decode subtitles and push them into the renderer (libass) */ + if (dec_ctx->subtitle_header) + ass_process_codec_private(ass->track, + dec_ctx->subtitle_header, + dec_ctx->subtitle_header_size); + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; + while (av_read_frame(fmt, &pkt) >= 0) { + int i, got_subtitle; + AVSubtitle sub; + + if (pkt.stream_index == sid) { + ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt); + if (ret < 0) { + av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n", + av_err2str(ret)); + } else if (got_subtitle) { + for (i = 0; i < sub.num_rects; i++) { + char *ass_line = sub.rects[i]->ass; + if (!ass_line) + break; + ass_process_data(ass->track, ass_line, strlen(ass_line)); + } + } + } + av_free_packet(&pkt); + avsubtitle_free(&sub); + } + +end: + av_dict_free(&codec_opts); + if (dec_ctx) + avcodec_close(dec_ctx); + if (fmt) + avformat_close_input(&fmt); + return ret; +} + +AVFilter avfilter_vf_subtitles = { + .name = "subtitles", + .description = NULL_IF_CONFIG_SMALL("Render text subtitles onto input video using the libass library."), + .priv_size = sizeof(AssContext), + .init = init_subtitles, + .uninit = uninit, + .query_formats = query_formats, + .inputs = ass_inputs, + .outputs = ass_outputs, + .priv_class = &subtitles_class, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_super2xsai.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_super2xsai.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2010 Niel van der Westhuizen + * Copyright (c) 2002 A'rpi + * Copyright (c) 1997-2001 ZSNES Team ( zsknight@zsnes.com / _demo_@zsnes.com ) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file + * Super 2xSaI video filter + * Ported from MPlayer libmpcodecs/vf_2xsai.c. + */ + +#include "libavutil/pixdesc.h" +#include "libavutil/intreadwrite.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct { + /* masks used for two pixels interpolation */ + uint32_t hi_pixel_mask; + uint32_t lo_pixel_mask; + + /* masks used for four pixels interpolation */ + uint32_t q_hi_pixel_mask; + uint32_t q_lo_pixel_mask; + + int bpp; ///< bytes per pixel, pixel stride for each (packed) pixel + int is_be; +} Super2xSaIContext; + +#define GET_RESULT(A, B, C, D) ((A != C || A != D) - (B != C || B != D)) + +#define INTERPOLATE(A, B) (((A & hi_pixel_mask) >> 1) + ((B & hi_pixel_mask) >> 1) + (A & B & lo_pixel_mask)) + +#define Q_INTERPOLATE(A, B, C, D) ((A & q_hi_pixel_mask) >> 2) + ((B & q_hi_pixel_mask) >> 2) + ((C & q_hi_pixel_mask) >> 2) + ((D & q_hi_pixel_mask) >> 2) \ + + ((((A & q_lo_pixel_mask) + (B & q_lo_pixel_mask) + (C & q_lo_pixel_mask) + (D & q_lo_pixel_mask)) >> 2) & q_lo_pixel_mask) + +static void super2xsai(AVFilterContext *ctx, + uint8_t *src, int src_linesize, + uint8_t *dst, int dst_linesize, + int width, int height) +{ + Super2xSaIContext *sai = ctx->priv; + unsigned int x, y; + uint32_t color[4][4]; + unsigned char *src_line[4]; + const int bpp = sai->bpp; + const uint32_t hi_pixel_mask = sai->hi_pixel_mask; + const uint32_t lo_pixel_mask = sai->lo_pixel_mask; + const uint32_t q_hi_pixel_mask = sai->q_hi_pixel_mask; + const uint32_t q_lo_pixel_mask = sai->q_lo_pixel_mask; + + /* Point to the first 4 lines, first line is duplicated */ + src_line[0] = src; + src_line[1] = src; + src_line[2] = src + src_linesize*FFMIN(1, height-1); + src_line[3] = src + src_linesize*FFMIN(2, height-1); + +#define READ_COLOR4(dst, src_line, off) dst = *((const uint32_t *)src_line + off) +#define READ_COLOR3(dst, src_line, off) dst = AV_RL24 (src_line + 3*off) +#define READ_COLOR2(dst, src_line, off) dst = sai->is_be ? AV_RB16(src_line + 2 * off) : AV_RL16(src_line + 2 * off) + + for (y = 0; y < height; y++) { + uint8_t *dst_line[2]; + + dst_line[0] = dst + dst_linesize*2*y; + dst_line[1] = dst + dst_linesize*(2*y+1); + + switch (bpp) { + case 4: + READ_COLOR4(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR4(color[0][2], src_line[0], 1); READ_COLOR4(color[0][3], src_line[0], 2); + READ_COLOR4(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR4(color[1][2], src_line[1], 1); READ_COLOR4(color[1][3], src_line[1], 2); + READ_COLOR4(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR4(color[2][2], src_line[2], 1); READ_COLOR4(color[2][3], src_line[2], 2); + READ_COLOR4(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR4(color[3][2], src_line[3], 1); READ_COLOR4(color[3][3], src_line[3], 2); + break; + case 3: + READ_COLOR3(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR3(color[0][2], src_line[0], 1); READ_COLOR3(color[0][3], src_line[0], 2); + READ_COLOR3(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR3(color[1][2], src_line[1], 1); READ_COLOR3(color[1][3], src_line[1], 2); + READ_COLOR3(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR3(color[2][2], src_line[2], 1); READ_COLOR3(color[2][3], src_line[2], 2); + READ_COLOR3(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR3(color[3][2], src_line[3], 1); READ_COLOR3(color[3][3], src_line[3], 2); + break; + default: + READ_COLOR2(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR2(color[0][2], src_line[0], 1); READ_COLOR2(color[0][3], src_line[0], 2); + READ_COLOR2(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR2(color[1][2], src_line[1], 1); READ_COLOR2(color[1][3], src_line[1], 2); + READ_COLOR2(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR2(color[2][2], src_line[2], 1); READ_COLOR2(color[2][3], src_line[2], 2); + READ_COLOR2(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR2(color[3][2], src_line[3], 1); READ_COLOR2(color[3][3], src_line[3], 2); + } + + for (x = 0; x < width; x++) { + uint32_t product1a, product1b, product2a, product2b; + +//--------------------------------------- B0 B1 B2 B3 0 1 2 3 +// 4 5* 6 S2 -> 4 5* 6 7 +// 1 2 3 S1 8 9 10 11 +// A0 A1 A2 A3 12 13 14 15 +//-------------------------------------- + if (color[2][1] == color[1][2] && color[1][1] != color[2][2]) { + product2b = color[2][1]; + product1b = product2b; + } else if (color[1][1] == color[2][2] && color[2][1] != color[1][2]) { + product2b = color[1][1]; + product1b = product2b; + } else if (color[1][1] == color[2][2] && color[2][1] == color[1][2]) { + int r = 0; + + r += GET_RESULT(color[1][2], color[1][1], color[1][0], color[3][1]); + r += GET_RESULT(color[1][2], color[1][1], color[2][0], color[0][1]); + r += GET_RESULT(color[1][2], color[1][1], color[3][2], color[2][3]); + r += GET_RESULT(color[1][2], color[1][1], color[0][2], color[1][3]); + + if (r > 0) + product1b = color[1][2]; + else if (r < 0) + product1b = color[1][1]; + else + product1b = INTERPOLATE(color[1][1], color[1][2]); + + product2b = product1b; + } else { + if (color[1][2] == color[2][2] && color[2][2] == color[3][1] && color[2][1] != color[3][2] && color[2][2] != color[3][0]) + product2b = Q_INTERPOLATE(color[2][2], color[2][2], color[2][2], color[2][1]); + else if (color[1][1] == color[2][1] && color[2][1] == color[3][2] && color[3][1] != color[2][2] && color[2][1] != color[3][3]) + product2b = Q_INTERPOLATE(color[2][1], color[2][1], color[2][1], color[2][2]); + else + product2b = INTERPOLATE(color[2][1], color[2][2]); + + if (color[1][2] == color[2][2] && color[1][2] == color[0][1] && color[1][1] != color[0][2] && color[1][2] != color[0][0]) + product1b = Q_INTERPOLATE(color[1][2], color[1][2], color[1][2], color[1][1]); + else if (color[1][1] == color[2][1] && color[1][1] == color[0][2] && color[0][1] != color[1][2] && color[1][1] != color[0][3]) + product1b = Q_INTERPOLATE(color[1][2], color[1][1], color[1][1], color[1][1]); + else + product1b = INTERPOLATE(color[1][1], color[1][2]); + } + + if (color[1][1] == color[2][2] && color[2][1] != color[1][2] && color[1][0] == color[1][1] && color[1][1] != color[3][2]) + product2a = INTERPOLATE(color[2][1], color[1][1]); + else if (color[1][1] == color[2][0] && color[1][2] == color[1][1] && color[1][0] != color[2][1] && color[1][1] != color[3][0]) + product2a = INTERPOLATE(color[2][1], color[1][1]); + else + product2a = color[2][1]; + + if (color[2][1] == color[1][2] && color[1][1] != color[2][2] && color[2][0] == color[2][1] && color[2][1] != color[0][2]) + product1a = INTERPOLATE(color[2][1], color[1][1]); + else if (color[1][0] == color[2][1] && color[2][2] == color[2][1] && color[2][0] != color[1][1] && color[2][1] != color[0][0]) + product1a = INTERPOLATE(color[2][1], color[1][1]); + else + product1a = color[1][1]; + + /* Set the calculated pixels */ + switch (bpp) { + case 4: + AV_WN32A(dst_line[0] + x * 8, product1a); + AV_WN32A(dst_line[0] + x * 8 + 4, product1b); + AV_WN32A(dst_line[1] + x * 8, product2a); + AV_WN32A(dst_line[1] + x * 8 + 4, product2b); + break; + case 3: + AV_WL24(dst_line[0] + x * 6, product1a); + AV_WL24(dst_line[0] + x * 6 + 3, product1b); + AV_WL24(dst_line[1] + x * 6, product2a); + AV_WL24(dst_line[1] + x * 6 + 3, product2b); + break; + default: // bpp = 2 + if (sai->is_be) { + AV_WB32(dst_line[0] + x * 4, product1a | (product1b << 16)); + AV_WB32(dst_line[1] + x * 4, product2a | (product2b << 16)); + } else { + AV_WL32(dst_line[0] + x * 4, product1a | (product1b << 16)); + AV_WL32(dst_line[1] + x * 4, product2a | (product2b << 16)); + } + } + + /* Move color matrix forward */ + color[0][0] = color[0][1]; color[0][1] = color[0][2]; color[0][2] = color[0][3]; + color[1][0] = color[1][1]; color[1][1] = color[1][2]; color[1][2] = color[1][3]; + color[2][0] = color[2][1]; color[2][1] = color[2][2]; color[2][2] = color[2][3]; + color[3][0] = color[3][1]; color[3][1] = color[3][2]; color[3][2] = color[3][3]; + + if (x < width - 3) { + x += 3; + switch (bpp) { + case 4: + READ_COLOR4(color[0][3], src_line[0], x); + READ_COLOR4(color[1][3], src_line[1], x); + READ_COLOR4(color[2][3], src_line[2], x); + READ_COLOR4(color[3][3], src_line[3], x); + break; + case 3: + READ_COLOR3(color[0][3], src_line[0], x); + READ_COLOR3(color[1][3], src_line[1], x); + READ_COLOR3(color[2][3], src_line[2], x); + READ_COLOR3(color[3][3], src_line[3], x); + break; + default: /* case 2 */ + READ_COLOR2(color[0][3], src_line[0], x); + READ_COLOR2(color[1][3], src_line[1], x); + READ_COLOR2(color[2][3], src_line[2], x); + READ_COLOR2(color[3][3], src_line[3], x); + } + x -= 3; + } + } + + /* We're done with one line, so we shift the source lines up */ + src_line[0] = src_line[1]; + src_line[1] = src_line[2]; + src_line[2] = src_line[3]; + + /* Read next line */ + src_line[3] = src_line[2]; + if (y < height - 3) + src_line[3] += src_linesize; + } // y loop +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_RGB565BE, AV_PIX_FMT_BGR565BE, AV_PIX_FMT_RGB555BE, AV_PIX_FMT_BGR555BE, + AV_PIX_FMT_RGB565LE, AV_PIX_FMT_BGR565LE, AV_PIX_FMT_RGB555LE, AV_PIX_FMT_BGR555LE, + AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + Super2xSaIContext *sai = inlink->dst->priv; + + sai->hi_pixel_mask = 0xFEFEFEFE; + sai->lo_pixel_mask = 0x01010101; + sai->q_hi_pixel_mask = 0xFCFCFCFC; + sai->q_lo_pixel_mask = 0x03030303; + sai->bpp = 4; + + switch (inlink->format) { + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + sai->bpp = 3; + break; + + case AV_PIX_FMT_RGB565BE: + case AV_PIX_FMT_BGR565BE: + sai->is_be = 1; + case AV_PIX_FMT_RGB565LE: + case AV_PIX_FMT_BGR565LE: + sai->hi_pixel_mask = 0xF7DEF7DE; + sai->lo_pixel_mask = 0x08210821; + sai->q_hi_pixel_mask = 0xE79CE79C; + sai->q_lo_pixel_mask = 0x18631863; + sai->bpp = 2; + break; + + case AV_PIX_FMT_BGR555BE: + case AV_PIX_FMT_RGB555BE: + sai->is_be = 1; + case AV_PIX_FMT_BGR555LE: + case AV_PIX_FMT_RGB555LE: + sai->hi_pixel_mask = 0x7BDE7BDE; + sai->lo_pixel_mask = 0x04210421; + sai->q_hi_pixel_mask = 0x739C739C; + sai->q_lo_pixel_mask = 0x0C630C63; + sai->bpp = 2; + break; + } + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterLink *inlink = outlink->src->inputs[0]; + + outlink->w = inlink->w*2; + outlink->h = inlink->h*2; + + av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n", + av_get_pix_fmt_name(inlink->format), + inlink->w, inlink->h, outlink->w, outlink->h); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) +{ + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFrame *outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!outpicref) { + av_frame_free(&inpicref); + return AVERROR(ENOMEM); + } + av_frame_copy_props(outpicref, inpicref); + outpicref->width = outlink->w; + outpicref->height = outlink->h; + + super2xsai(inlink->dst, inpicref->data[0], inpicref->linesize[0], + outpicref->data[0], outpicref->linesize[0], + inlink->w, inlink->h); + + av_frame_free(&inpicref); + return ff_filter_frame(outlink, outpicref); +} + +static const AVFilterPad super2xsai_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad super2xsai_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter avfilter_vf_super2xsai = { + .name = "super2xsai", + .description = NULL_IF_CONFIG_SMALL("Scale the input by 2x using the Super2xSaI pixel art algorithm."), + .priv_size = sizeof(Super2xSaIContext), + .query_formats = query_formats, + .inputs = super2xsai_inputs, + .outputs = super2xsai_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_swapuv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_swapuv.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2002 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * swap UV filter + */ + +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +static void do_swap(AVFrame *frame) +{ + FFSWAP(uint8_t*, frame->data[1], frame->data[2]); + FFSWAP(int, frame->linesize[1], frame->linesize[2]); + FFSWAP(uint64_t, frame->error[1], frame->error[2]); + FFSWAP(AVBufferRef*, frame->buf[1], frame->buf[2]); +} + +static AVFrame *get_video_buffer(AVFilterLink *link, int w, int h) +{ + AVFrame *picref = ff_default_get_video_buffer(link, w, h); + do_swap(picref); + return picref; +} + +static int filter_frame(AVFilterLink *link, AVFrame *inpicref) +{ + do_swap(inpicref); + return ff_filter_frame(link->dst->outputs[0], inpicref); +} + +static int is_planar_yuv(const AVPixFmtDescriptor *desc) +{ + int i; + + if (desc->flags & ~(PIX_FMT_BE | PIX_FMT_PLANAR | PIX_FMT_ALPHA) || + desc->nb_components < 3 || + (desc->comp[1].depth_minus1 != desc->comp[2].depth_minus1)) + return 0; + for (i = 0; i < desc->nb_components; i++) { + if (desc->comp[i].offset_plus1 != 1 || + desc->comp[i].shift != 0 || + desc->comp[i].plane != i) + return 0; + } + + return 1; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + int fmt; + + for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (is_planar_yuv(desc)) + ff_add_format(&formats, fmt); + } + + ff_set_common_formats(ctx, formats); + return 0; +} + +static const AVFilterPad swapuv_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = get_video_buffer, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad swapuv_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter avfilter_vf_swapuv = { + .name = "swapuv", + .description = NULL_IF_CONFIG_SMALL("Swap U and V components."), + .priv_size = 0, + .query_formats = query_formats, + .inputs = swapuv_inputs, + .outputs = swapuv_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_swapuv.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_swapuv.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,16 @@ +libavfilter/vf_swapuv.o libavfilter/vf_swapuv.o: libavfilter/vf_swapuv.c \ + libavutil/pixdesc.h libavutil/pixfmt.h libavutil/avconfig.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavfilter/avfilter.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/formats.h \ + libavfilter/internal.h libavfilter/avfiltergraph.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_swapuv.o Binary file ffmpeg/libavfilter/vf_swapuv.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_telecine.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_telecine.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012 Rudolf Polzer + * Copyright (c) 2013 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file telecine filter, heavily based from mpv-player:TOOLS/vf_dlopen/telecine.c by + * Rudolf Polzer. + */ + +#include "libavutil/avstring.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct { + const AVClass *class; + int first_field; + char *pattern; + unsigned int pattern_pos; + + AVRational pts; + double ts_unit; + int out_cnt; + int occupied; + int64_t frame_count; + + int nb_planes; + int planeheight[4]; + int stride[4]; + + AVFrame *frame[5]; + AVFrame *temp; +} TelecineContext; + +#define OFFSET(x) offsetof(TelecineContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption telecine_options[] = { + {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"}, + {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, + {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, + {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, + {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, + {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS}, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(telecine); + +static av_cold int init(AVFilterContext *ctx) +{ + TelecineContext *tc = ctx->priv; + const char *p; + int max = 0; + + if (!strlen(tc->pattern)) { + av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n"); + return AVERROR_INVALIDDATA; + } + + for (p = tc->pattern; *p; p++) { + if (!av_isdigit(*p)) { + av_log(ctx, AV_LOG_ERROR, "Provided pattern includes non-numeric characters.\n"); + return AVERROR_INVALIDDATA; + } + + max = FFMAX(*p - '0', max); + tc->pts.num += 2; + tc->pts.den += *p - '0'; + } + + tc->out_cnt = (max + 1) / 2; + av_log(ctx, AV_LOG_INFO, "Telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n", + tc->pattern, tc->out_cnt, tc->pts.num, tc->pts.den); + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *pix_fmts = NULL; + int fmt; + + for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!(desc->flags & PIX_FMT_HWACCEL)) + ff_add_format(&pix_fmts, fmt); + } + + ff_set_common_formats(ctx, pix_fmts); + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + TelecineContext *tc = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int i, ret; + + tc->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h); + if (!tc->temp) + return AVERROR(ENOMEM); + for (i = 0; i < tc->out_cnt; i++) { + tc->frame[i] = ff_get_video_buffer(inlink, inlink->w, inlink->h); + if (!tc->frame[i]) + return AVERROR(ENOMEM); + } + + if ((ret = av_image_fill_linesizes(tc->stride, inlink->format, inlink->w)) < 0) + return ret; + + tc->planeheight[1] = tc->planeheight[2] = inlink->h >> desc->log2_chroma_h; + tc->planeheight[0] = tc->planeheight[3] = inlink->h; + + tc->nb_planes = av_pix_fmt_count_planes(inlink->format); + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TelecineContext *tc = ctx->priv; + const AVFilterLink *inlink = ctx->inputs[0]; + AVRational fps = inlink->frame_rate; + + if (!fps.num || !fps.den) { + av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; " + "current rate of %d/%d is invalid\n", fps.num, fps.den); + return AVERROR(EINVAL); + } + fps = av_mul_q(fps, av_inv_q(tc->pts)); + av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n", + inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den); + + outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; + outlink->frame_rate = fps; + outlink->time_base = av_mul_q(inlink->time_base, tc->pts); + + tc->ts_unit = av_q2d(av_inv_q(av_mul_q(fps, outlink->time_base))); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + TelecineContext *tc = ctx->priv; + int i, len, ret = 0, nout = 0; + + len = tc->pattern[tc->pattern_pos] - '0'; + + tc->pattern_pos++; + if (!tc->pattern[tc->pattern_pos]) + tc->pattern_pos = 0; + + if (!len) { // do not output any field from this frame + av_frame_free(&inpicref); + return 0; + } + + if (tc->occupied) { + for (i = 0; i < tc->nb_planes; i++) { + // fill in the EARLIER field from the buffered pic + av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * tc->first_field, + tc->frame[nout]->linesize[i] * 2, + tc->temp->data[i] + tc->temp->linesize[i] * tc->first_field, + tc->temp->linesize[i] * 2, + tc->stride[i], + (tc->planeheight[i] - tc->first_field + 1) / 2); + // fill in the LATER field from the new pic + av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * !tc->first_field, + tc->frame[nout]->linesize[i] * 2, + inpicref->data[i] + inpicref->linesize[i] * !tc->first_field, + inpicref->linesize[i] * 2, + tc->stride[i], + (tc->planeheight[i] - !tc->first_field + 1) / 2); + } + nout++; + len--; + tc->occupied = 0; + } + + while (len >= 2) { + // output THIS image as-is + for (i = 0; i < tc->nb_planes; i++) + av_image_copy_plane(tc->frame[nout]->data[i], tc->frame[nout]->linesize[i], + inpicref->data[i], inpicref->linesize[i], + tc->stride[i], + tc->planeheight[i]); + nout++; + len -= 2; + } + + if (len >= 1) { + // copy THIS image to the buffer, we need it later + for (i = 0; i < tc->nb_planes; i++) + av_image_copy_plane(tc->temp->data[i], tc->temp->linesize[i], + inpicref->data[i], inpicref->linesize[i], + tc->stride[i], + tc->planeheight[i]); + tc->occupied = 1; + } + + for (i = 0; i < nout; i++) { + AVFrame *frame = av_frame_clone(tc->frame[i]); + + if (!frame) { + av_frame_free(&inpicref); + return AVERROR(ENOMEM); + } + + av_frame_copy_props(frame, inpicref); + frame->pts = tc->frame_count++ * tc->ts_unit; + ret = ff_filter_frame(outlink, frame); + } + av_frame_free(&inpicref); + + return ret; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + TelecineContext *tc = ctx->priv; + int i; + + av_frame_free(&tc->temp); + for (i = 0; i < tc->out_cnt; i++) + av_frame_free(&tc->frame[i]); +} + +static const AVFilterPad telecine_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad telecine_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter avfilter_vf_telecine = { + .name = "telecine", + .description = NULL_IF_CONFIG_SMALL("Apply a telecine pattern."), + .priv_size = sizeof(TelecineContext), + .priv_class = &telecine_class, + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = telecine_inputs, + .outputs = telecine_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_telecine.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_telecine.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavfilter/vf_telecine.o libavfilter/vf_telecine.o: \ + libavfilter/vf_telecine.c libavutil/avstring.h libavutil/attributes.h \ + libavutil/imgutils.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/pixdesc.h \ + libavutil/opt.h libavutil/samplefmt.h libavutil/pixdesc.h \ + libavfilter/avfilter.h libavutil/avutil.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/log.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavfilter/version.h \ + libavutil/avutil.h libavfilter/formats.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_telecine.o Binary file ffmpeg/libavfilter/vf_telecine.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_thumbnail.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_thumbnail.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2011 Smartjog S.A.S, Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Potential thumbnail lookup filter to reduce the risk of an inappropriate + * selection (such as a black frame) we could get with an absolute seek. + * + * Simplified version of algorithm by Vadim Zaliva . + * @see http://notbrainsurgery.livejournal.com/29773.html + */ + +#include "libavutil/opt.h" +#include "avfilter.h" +#include "internal.h" + +#define HIST_SIZE (3*256) + +struct thumb_frame { + AVFrame *buf; ///< cached frame + int histogram[HIST_SIZE]; ///< RGB color distribution histogram of the frame +}; + +typedef struct { + const AVClass *class; + int n; ///< current frame + int n_frames; ///< number of frames for analysis + struct thumb_frame *frames; ///< the n_frames frames + AVRational tb; ///< copy of the input timebase to ease access +} ThumbContext; + +#define OFFSET(x) offsetof(ThumbContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption thumbnail_options[] = { + { "n", "set the frames batch size", OFFSET(n_frames), AV_OPT_TYPE_INT, {.i64=100}, 2, INT_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(thumbnail); + +static av_cold int init(AVFilterContext *ctx) +{ + ThumbContext *thumb = ctx->priv; + + thumb->frames = av_calloc(thumb->n_frames, sizeof(*thumb->frames)); + if (!thumb->frames) { + av_log(ctx, AV_LOG_ERROR, + "Allocation failure, try to lower the number of frames\n"); + return AVERROR(ENOMEM); + } + av_log(ctx, AV_LOG_VERBOSE, "batch size: %d frames\n", thumb->n_frames); + return 0; +} + +/** + * @brief Compute Sum-square deviation to estimate "closeness". + * @param hist color distribution histogram + * @param median average color distribution histogram + * @return sum of squared errors + */ +static double frame_sum_square_err(const int *hist, const double *median) +{ + int i; + double err, sum_sq_err = 0; + + for (i = 0; i < HIST_SIZE; i++) { + err = median[i] - (double)hist[i]; + sum_sq_err += err*err; + } + return sum_sq_err; +} + +static AVFrame *get_best_frame(AVFilterContext *ctx) +{ + AVFrame *picref; + ThumbContext *thumb = ctx->priv; + int i, j, best_frame_idx = 0; + int nb_frames = thumb->n; + double avg_hist[HIST_SIZE] = {0}, sq_err, min_sq_err = -1; + + // average histogram of the N frames + for (j = 0; j < FF_ARRAY_ELEMS(avg_hist); j++) { + for (i = 0; i < nb_frames; i++) + avg_hist[j] += (double)thumb->frames[i].histogram[j]; + avg_hist[j] /= nb_frames; + } + + // find the frame closer to the average using the sum of squared errors + for (i = 0; i < nb_frames; i++) { + sq_err = frame_sum_square_err(thumb->frames[i].histogram, avg_hist); + if (i == 0 || sq_err < min_sq_err) + best_frame_idx = i, min_sq_err = sq_err; + } + + // free and reset everything (except the best frame buffer) + for (i = 0; i < nb_frames; i++) { + memset(thumb->frames[i].histogram, 0, sizeof(thumb->frames[i].histogram)); + if (i != best_frame_idx) + av_frame_free(&thumb->frames[i].buf); + } + thumb->n = 0; + + // raise the chosen one + picref = thumb->frames[best_frame_idx].buf; + av_log(ctx, AV_LOG_INFO, "frame id #%d (pts_time=%f) selected " + "from a set of %d images\n", best_frame_idx, + picref->pts * av_q2d(thumb->tb), nb_frames); + thumb->frames[best_frame_idx].buf = NULL; + + return picref; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + int i, j; + AVFilterContext *ctx = inlink->dst; + ThumbContext *thumb = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int *hist = thumb->frames[thumb->n].histogram; + const uint8_t *p = frame->data[0]; + + // keep a reference of each frame + thumb->frames[thumb->n].buf = frame; + + // update current frame RGB histogram + for (j = 0; j < inlink->h; j++) { + for (i = 0; i < inlink->w; i++) { + hist[0*256 + p[i*3 ]]++; + hist[1*256 + p[i*3 + 1]]++; + hist[2*256 + p[i*3 + 2]]++; + } + p += frame->linesize[0]; + } + + // no selection until the buffer of N frames is filled up + thumb->n++; + if (thumb->n < thumb->n_frames) + return 0; + + return ff_filter_frame(outlink, get_best_frame(ctx)); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + int i; + ThumbContext *thumb = ctx->priv; + for (i = 0; i < thumb->n_frames && thumb->frames[i].buf; i++) + av_frame_free(&thumb->frames[i].buf); + av_freep(&thumb->frames); +} + +static int request_frame(AVFilterLink *link) +{ + AVFilterContext *ctx = link->src; + ThumbContext *thumb = ctx->priv; + + /* loop until a frame thumbnail is available (when a frame is queued, + * thumb->n is reset to zero) */ + do { + int ret = ff_request_frame(ctx->inputs[0]); + if (ret == AVERROR_EOF && thumb->n) { + ret = ff_filter_frame(link, get_best_frame(ctx)); + if (ret < 0) + return ret; + ret = AVERROR_EOF; + } + if (ret < 0) + return ret; + } while (thumb->n); + return 0; +} + +static int config_props(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ThumbContext *thumb = ctx->priv; + + thumb->tb = inlink->time_base; + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_NONE + }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static const AVFilterPad thumbnail_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + .get_video_buffer = ff_null_get_video_buffer, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad thumbnail_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + }, + { NULL } +}; + +AVFilter avfilter_vf_thumbnail = { + .name = "thumbnail", + .description = NULL_IF_CONFIG_SMALL("Select the most representative frame in a given sequence of consecutive frames."), + .priv_size = sizeof(ThumbContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = thumbnail_inputs, + .outputs = thumbnail_outputs, + .priv_class = &thumbnail_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_thumbnail.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_thumbnail.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,16 @@ +libavfilter/vf_thumbnail.o libavfilter/vf_thumbnail.o: \ + libavfilter/vf_thumbnail.c libavutil/opt.h libavutil/rational.h \ + libavutil/attributes.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/formats.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_thumbnail.o Binary file ffmpeg/libavfilter/vf_thumbnail.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_tile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_tile.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * tile video filter + */ + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "drawutils.h" +#include "formats.h" +#include "video.h" +#include "internal.h" + +typedef struct { + const AVClass *class; + unsigned w, h; + unsigned margin; + unsigned padding; + unsigned current; + unsigned nb_frames; + FFDrawContext draw; + FFDrawColor blank; + AVFrame *out_ref; +} TileContext; + +#define REASONABLE_SIZE 1024 + +#define OFFSET(x) offsetof(TileContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption tile_options[] = { + { "layout", "set grid size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, + {.str = "6x5"}, 0, 0, FLAGS }, + { "nb_frames", "set maximum number of frame to render", OFFSET(nb_frames), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, + { "margin", "set outer border margin in pixels", OFFSET(margin), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS }, + { "padding", "set inner border thickness in pixels", OFFSET(padding), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS }, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(tile); + +static av_cold int init(AVFilterContext *ctx) +{ + TileContext *tile = ctx->priv; + + if (tile->w > REASONABLE_SIZE || tile->h > REASONABLE_SIZE) { + av_log(ctx, AV_LOG_ERROR, "Tile size %ux%u is insane.\n", + tile->w, tile->h); + return AVERROR(EINVAL); + } + + if (tile->nb_frames == 0) { + tile->nb_frames = tile->w * tile->h; + } else if (tile->nb_frames > tile->w * tile->h) { + av_log(ctx, AV_LOG_ERROR, "nb_frames must be less than or equal to %dx%d=%d\n", + tile->w, tile->h, tile->w * tile->h); + return AVERROR(EINVAL); + } + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + return 0; +} + +static int config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TileContext *tile = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const unsigned total_margin_w = (tile->w - 1) * tile->padding + 2*tile->margin; + const unsigned total_margin_h = (tile->h - 1) * tile->padding + 2*tile->margin; + + if (inlink->w > (INT_MAX - total_margin_w) / tile->w) { + av_log(ctx, AV_LOG_ERROR, "Total width %ux%u is too much.\n", + tile->w, inlink->w); + return AVERROR(EINVAL); + } + if (inlink->h > (INT_MAX - total_margin_h) / tile->h) { + av_log(ctx, AV_LOG_ERROR, "Total height %ux%u is too much.\n", + tile->h, inlink->h); + return AVERROR(EINVAL); + } + outlink->w = tile->w * inlink->w + total_margin_w; + outlink->h = tile->h * inlink->h + total_margin_h; + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + outlink->frame_rate = av_mul_q(inlink->frame_rate, + (AVRational){ 1, tile->nb_frames }); + ff_draw_init(&tile->draw, inlink->format, 0); + /* TODO make the color an option, or find an unified way of choosing it */ + ff_draw_color(&tile->draw, &tile->blank, (uint8_t[]){ 0, 0, 0, -1 }); + + outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; + + return 0; +} + +static void get_current_tile_pos(AVFilterContext *ctx, unsigned *x, unsigned *y) +{ + TileContext *tile = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const unsigned tx = tile->current % tile->w; + const unsigned ty = tile->current / tile->w; + + *x = tile->margin + (inlink->w + tile->padding) * tx; + *y = tile->margin + (inlink->h + tile->padding) * ty; +} + +static void draw_blank_frame(AVFilterContext *ctx, AVFrame *out_buf) +{ + TileContext *tile = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + unsigned x0, y0; + + get_current_tile_pos(ctx, &x0, &y0); + ff_fill_rectangle(&tile->draw, &tile->blank, + out_buf->data, out_buf->linesize, + x0, y0, inlink->w, inlink->h); + tile->current++; +} +static int end_last_frame(AVFilterContext *ctx) +{ + TileContext *tile = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out_buf = tile->out_ref; + int ret; + + while (tile->current < tile->nb_frames) + draw_blank_frame(ctx, out_buf); + ret = ff_filter_frame(outlink, out_buf); + tile->current = 0; + return ret; +} + +/* Note: direct rendering is not possible since there is no guarantee that + * buffers are fed to filter_frame in the order they were obtained from + * get_buffer (think B-frames). */ + +static int filter_frame(AVFilterLink *inlink, AVFrame *picref) +{ + AVFilterContext *ctx = inlink->dst; + TileContext *tile = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + unsigned x0, y0; + + if (!tile->current) { + tile->out_ref = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!tile->out_ref) + return AVERROR(ENOMEM); + av_frame_copy_props(tile->out_ref, picref); + tile->out_ref->width = outlink->w; + tile->out_ref->height = outlink->h; + + /* fill surface once for margin/padding */ + if (tile->margin || tile->padding) + ff_fill_rectangle(&tile->draw, &tile->blank, + tile->out_ref->data, + tile->out_ref->linesize, + 0, 0, outlink->w, outlink->h); + } + + get_current_tile_pos(ctx, &x0, &y0); + ff_copy_rectangle2(&tile->draw, + tile->out_ref->data, tile->out_ref->linesize, + picref->data, picref->linesize, + x0, y0, 0, 0, inlink->w, inlink->h); + + av_frame_free(&picref); + if (++tile->current == tile->nb_frames) + return end_last_frame(ctx); + + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TileContext *tile = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + int r; + + r = ff_request_frame(inlink); + if (r == AVERROR_EOF && tile->current) + r = end_last_frame(ctx); + return r; +} + +static const AVFilterPad tile_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad tile_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + .request_frame = request_frame, + }, + { NULL } +}; + +AVFilter avfilter_vf_tile = { + .name = "tile", + .description = NULL_IF_CONFIG_SMALL("Tile several successive frames together."), + .init = init, + .query_formats = query_formats, + .priv_size = sizeof(TileContext), + .inputs = tile_inputs, + .outputs = tile_outputs, + .priv_class = &tile_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_tile.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_tile.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,17 @@ +libavfilter/vf_tile.o libavfilter/vf_tile.o: libavfilter/vf_tile.c \ + libavutil/opt.h libavutil/rational.h libavutil/attributes.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/pixdesc.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/drawutils.h \ + libavfilter/formats.h libavfilter/video.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_tile.o Binary file ffmpeg/libavfilter/vf_tile.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_tinterlace.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_tinterlace.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * Copyright (c) 2010 Baptiste Coudurier + * Copyright (c) 2003 Michael Zucchi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file + * temporal field interlace filter, ported from MPlayer/libmpcodecs + */ + +#include "libavutil/opt.h" +#include "libavutil/imgutils.h" +#include "libavutil/avassert.h" +#include "avfilter.h" +#include "internal.h" + +enum TInterlaceMode { + MODE_MERGE = 0, + MODE_DROP_EVEN, + MODE_DROP_ODD, + MODE_PAD, + MODE_INTERLEAVE_TOP, + MODE_INTERLEAVE_BOTTOM, + MODE_INTERLACEX2, + MODE_NB, +}; + +typedef struct { + const AVClass *class; + enum TInterlaceMode mode; ///< interlace mode selected + int flags; ///< flags affecting interlacing algorithm + int frame; ///< number of the output frame + int vsub; ///< chroma vertical subsampling + AVFrame *cur; + AVFrame *next; + uint8_t *black_data[4]; ///< buffer used to fill padded lines + int black_linesize[4]; +} TInterlaceContext; + +#define OFFSET(x) offsetof(TInterlaceContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define TINTERLACE_FLAG_VLPF 01 + +static const AVOption tinterlace_options[] = { + {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"}, + {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, + + {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, "flags" }, + {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, + {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, + + {NULL} +}; + +AVFILTER_DEFINE_CLASS(tinterlace); + +#define FULL_SCALE_YUVJ_FORMATS \ + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P + +static enum AVPixelFormat full_scale_yuvj_pix_fmts[] = { + FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE +}; + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS, + AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + TInterlaceContext *tinterlace = ctx->priv; + + av_frame_free(&tinterlace->cur ); + av_frame_free(&tinterlace->next); + av_freep(&tinterlace->black_data[0]); +} + +static int config_out_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = outlink->src->inputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + TInterlaceContext *tinterlace = ctx->priv; + + tinterlace->vsub = desc->log2_chroma_h; + outlink->w = inlink->w; + outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD ? + inlink->h*2 : inlink->h; + + if (tinterlace->mode == MODE_PAD) { + uint8_t black[4] = { 16, 128, 128, 16 }; + int i, ret; + if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts)) + black[0] = black[3] = 0; + ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize, + outlink->w, outlink->h, outlink->format, 1); + if (ret < 0) + return ret; + + /* fill black picture with black */ + for (i = 0; i < 4 && tinterlace->black_data[i]; i++) { + int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h; + memset(tinterlace->black_data[i], black[i], + tinterlace->black_linesize[i] * h); + } + } + if ((tinterlace->flags & TINTERLACE_FLAG_VLPF) + && !(tinterlace->mode == MODE_INTERLEAVE_TOP + || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) { + av_log(ctx, AV_LOG_WARNING, "low_pass_filter flag ignored with mode %d\n", + tinterlace->mode); + tinterlace->flags &= ~TINTERLACE_FLAG_VLPF; + } + av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", + tinterlace->mode, (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "on" : "off", + inlink->h, outlink->h); + + return 0; +} + +#define FIELD_UPPER 0 +#define FIELD_LOWER 1 +#define FIELD_UPPER_AND_LOWER 2 + +/** + * Copy picture field from src to dst. + * + * @param src_field copy from upper, lower field or both + * @param interleave leave a padding line between each copied line + * @param dst_field copy to upper or lower field, + * only meaningful when interleave is selected + * @param flags context flags + */ +static inline +void copy_picture_field(uint8_t *dst[4], int dst_linesize[4], + const uint8_t *src[4], int src_linesize[4], + enum AVPixelFormat format, int w, int src_h, + int src_field, int interleave, int dst_field, + int flags) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); + int plane, vsub = desc->log2_chroma_h; + int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2; + int h, i; + + for (plane = 0; plane < desc->nb_components; plane++) { + int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h; + int linesize = av_image_get_linesize(format, w, plane); + uint8_t *dstp = dst[plane]; + const uint8_t *srcp = src[plane]; + + if (linesize < 0) + return; + + lines /= k; + if (src_field == FIELD_LOWER) + srcp += src_linesize[plane]; + if (interleave && dst_field == FIELD_LOWER) + dstp += dst_linesize[plane]; + if (flags & TINTERLACE_FLAG_VLPF) { + // Low-pass filtering is required when creating an interlaced destination from + // a progressive source which contains high-frequency vertical detail. + // Filtering will reduce interlace 'twitter' and Moire patterning. + int srcp_linesize = src_linesize[plane] * k; + int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1); + for (h = lines; h > 0; h--) { + const uint8_t *srcp_above = srcp - src_linesize[plane]; + const uint8_t *srcp_below = srcp + src_linesize[plane]; + if (h == lines) srcp_above = srcp; // there is no line above + if (h == 1) srcp_below = srcp; // there is no line below + for (i = 0; i < linesize; i++) { + // this calculation is an integer representation of + // '0.5 * current + 0.25 * above + 0.25 + below' + // '1 +' is for rounding. */ + dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2; + } + dstp += dstp_linesize; + srcp += srcp_linesize; + } + } else { + av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1), + srcp, src_linesize[plane]*k, linesize, lines); + } + } +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *picref) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + TInterlaceContext *tinterlace = ctx->priv; + AVFrame *cur, *next, *out; + int field, tff, ret; + + av_frame_free(&tinterlace->cur); + tinterlace->cur = tinterlace->next; + tinterlace->next = picref; + + cur = tinterlace->cur; + next = tinterlace->next; + /* we need at least two frames */ + if (!tinterlace->cur) + return 0; + + switch (tinterlace->mode) { + case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into + * the lower field, generating a double-height video at half framerate */ + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, cur); + out->height = outlink->h; + out->interlaced_frame = 1; + out->top_field_first = 1; + + /* write odd frame lines into the upper field of the new frame */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)cur->data, cur->linesize, + inlink->format, inlink->w, inlink->h, + FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER, tinterlace->flags); + /* write even frame lines into the lower field of the new frame */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)next->data, next->linesize, + inlink->format, inlink->w, inlink->h, + FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER, tinterlace->flags); + av_frame_free(&tinterlace->next); + break; + + case MODE_DROP_ODD: /* only output even frames, odd frames are dropped; height unchanged, half framerate */ + case MODE_DROP_EVEN: /* only output odd frames, even frames are dropped; height unchanged, half framerate */ + out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next); + av_frame_free(&tinterlace->next); + break; + + case MODE_PAD: /* expand each frame to double height, but pad alternate + * lines with black; framerate unchanged */ + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, cur); + out->height = outlink->h; + + field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER; + /* copy upper and lower fields */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)cur->data, cur->linesize, + inlink->format, inlink->w, inlink->h, + FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags); + /* pad with black the other field */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)tinterlace->black_data, tinterlace->black_linesize, + inlink->format, inlink->w, inlink->h, + FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags); + break; + + /* interleave upper/lower lines from odd frames with lower/upper lines from even frames, + * halving the frame rate and preserving image height */ + case MODE_INTERLEAVE_TOP: /* top field first */ + case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ + tff = tinterlace->mode == MODE_INTERLEAVE_TOP; + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, cur); + out->interlaced_frame = 1; + out->top_field_first = tff; + + /* copy upper/lower field from cur */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)cur->data, cur->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, + tinterlace->flags); + /* copy lower/upper field from next */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)next->data, next->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, + tinterlace->flags); + av_frame_free(&tinterlace->next); + break; + case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */ + /* output current frame first */ + out = av_frame_clone(cur); + if (!out) + return AVERROR(ENOMEM); + out->interlaced_frame = 1; + + if ((ret = ff_filter_frame(outlink, out)) < 0) + return ret; + + /* output mix of current and next frame */ + tff = next->top_field_first; + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, next); + out->interlaced_frame = 1; + + /* write current frame second field lines into the second field of the new frame */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)cur->data, cur->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, + tinterlace->flags); + /* write next frame first field lines into the first field of the new frame */ + copy_picture_field(out->data, out->linesize, + (const uint8_t **)next->data, next->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, + tinterlace->flags); + break; + default: + av_assert0(0); + } + + ret = ff_filter_frame(outlink, out); + tinterlace->frame++; + + return ret; +} + +static int request_frame(AVFilterLink *outlink) +{ + TInterlaceContext *tinterlace = outlink->src->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + + do { + int ret; + + if ((ret = ff_request_frame(inlink)) < 0) + return ret; + } while (!tinterlace->cur); + + return 0; +} + +static const AVFilterPad tinterlace_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad tinterlace_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_out_props, + .request_frame = request_frame, + }, + { NULL } +}; + +AVFilter avfilter_vf_tinterlace = { + .name = "tinterlace", + .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."), + .priv_size = sizeof(TInterlaceContext), + .uninit = uninit, + .query_formats = query_formats, + .inputs = tinterlace_inputs, + .outputs = tinterlace_outputs, + .priv_class = &tinterlace_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_transpose.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_transpose.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2010 Stefano Sabatini + * Copyright (c) 2008 Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * transposition filter + * Based on MPlayer libmpcodecs/vf_rotate.c. + */ + +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/imgutils.h" +#include "libavutil/internal.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef enum { + TRANSPOSE_PT_TYPE_NONE, + TRANSPOSE_PT_TYPE_LANDSCAPE, + TRANSPOSE_PT_TYPE_PORTRAIT, +} PassthroughType; + +enum TransposeDir { + TRANSPOSE_CCLOCK_FLIP, + TRANSPOSE_CLOCK, + TRANSPOSE_CCLOCK, + TRANSPOSE_CLOCK_FLIP, +}; + +typedef struct { + const AVClass *class; + int hsub, vsub; + int pixsteps[4]; + + PassthroughType passthrough; ///< landscape passthrough mode enabled + enum TransposeDir dir; +} TransContext; + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *pix_fmts = NULL; + int fmt; + + for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!(desc->flags & PIX_FMT_PAL || + desc->flags & PIX_FMT_HWACCEL || + desc->flags & PIX_FMT_BITSTREAM || + desc->log2_chroma_w != desc->log2_chroma_h)) + ff_add_format(&pix_fmts, fmt); + } + + + ff_set_common_formats(ctx, pix_fmts); + return 0; +} + +static int config_props_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TransContext *trans = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format); + const AVPixFmtDescriptor *desc_in = av_pix_fmt_desc_get(inlink->format); + + if (trans->dir&4) { + av_log(ctx, AV_LOG_WARNING, + "dir values greater than 3 are deprecated, use the passthrough option instead\n"); + trans->dir &= 3; + trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE; + } + + if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) || + (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) { + av_log(ctx, AV_LOG_VERBOSE, + "w:%d h:%d -> w:%d h:%d (passthrough mode)\n", + inlink->w, inlink->h, inlink->w, inlink->h); + return 0; + } else { + trans->passthrough = TRANSPOSE_PT_TYPE_NONE; + } + + trans->hsub = desc_in->log2_chroma_w; + trans->vsub = desc_in->log2_chroma_h; + + av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out); + + outlink->w = inlink->h; + outlink->h = inlink->w; + + if (inlink->sample_aspect_ratio.num){ + outlink->sample_aspect_ratio = av_div_q((AVRational){1,1}, inlink->sample_aspect_ratio); + } else + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n", + inlink->w, inlink->h, trans->dir, outlink->w, outlink->h, + trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise", + trans->dir == 0 || trans->dir == 3); + return 0; +} + +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + TransContext *trans = inlink->dst->priv; + + return trans->passthrough ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + TransContext *trans = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFrame *out; + int plane; + + if (trans->passthrough) + return ff_filter_frame(outlink, in); + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + + out->pts = in->pts; + + if (in->sample_aspect_ratio.num == 0) { + out->sample_aspect_ratio = in->sample_aspect_ratio; + } else { + out->sample_aspect_ratio.num = in->sample_aspect_ratio.den; + out->sample_aspect_ratio.den = in->sample_aspect_ratio.num; + } + + for (plane = 0; out->data[plane]; plane++) { + int hsub = plane == 1 || plane == 2 ? trans->hsub : 0; + int vsub = plane == 1 || plane == 2 ? trans->vsub : 0; + int pixstep = trans->pixsteps[plane]; + int inh = in->height >> vsub; + int outw = out->width >> hsub; + int outh = out->height >> vsub; + uint8_t *dst, *src; + int dstlinesize, srclinesize; + int x, y; + + dst = out->data[plane]; + dstlinesize = out->linesize[plane]; + src = in->data[plane]; + srclinesize = in->linesize[plane]; + + if (trans->dir&1) { + src += in->linesize[plane] * (inh-1); + srclinesize *= -1; + } + + if (trans->dir&2) { + dst += out->linesize[plane] * (outh-1); + dstlinesize *= -1; + } + + for (y = 0; y < outh; y++) { + switch (pixstep) { + case 1: + for (x = 0; x < outw; x++) + dst[x] = src[x*srclinesize + y]; + break; + case 2: + for (x = 0; x < outw; x++) + *((uint16_t *)(dst + 2*x)) = *((uint16_t *)(src + x*srclinesize + y*2)); + break; + case 3: + for (x = 0; x < outw; x++) { + int32_t v = AV_RB24(src + x*srclinesize + y*3); + AV_WB24(dst + 3*x, v); + } + break; + case 4: + for (x = 0; x < outw; x++) + *((uint32_t *)(dst + 4*x)) = *((uint32_t *)(src + x*srclinesize + y*4)); + break; + case 6: + for (x = 0; x < outw; x++) { + int64_t v = AV_RB48(src + x*srclinesize + y*6); + AV_WB48(dst + 6*x, v); + } + break; + case 8: + for (x = 0; x < outw; x++) + *((uint64_t *)(dst + 8*x)) = *((uint64_t *)(src + x*srclinesize + y*8)); + break; + } + dst += dstlinesize; + } + } + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +#define OFFSET(x) offsetof(TransContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption transpose_options[] = { + { "dir", "Transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, + TRANSPOSE_CCLOCK_FLIP, TRANSPOSE_CLOCK_FLIP, FLAGS, "dir" }, + { "cclock_flip", "counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .unit = "dir" }, + { "clock", "clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .unit = "dir" }, + { "cclock", "counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .unit = "dir" }, + { "clock_flip", "clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .unit = "dir" }, + + { "passthrough", "do not apply transposition if the input matches the specified geometry", + OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(transpose); + +static const AVFilterPad avfilter_vf_transpose_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer= get_video_buffer, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad avfilter_vf_transpose_outputs[] = { + { + .name = "default", + .config_props = config_props_output, + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter avfilter_vf_transpose = { + .name = "transpose", + .description = NULL_IF_CONFIG_SMALL("Transpose input video."), + + .priv_size = sizeof(TransContext), + .priv_class = &transpose_class, + + .query_formats = query_formats, + + .inputs = avfilter_vf_transpose_inputs, + .outputs = avfilter_vf_transpose_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_transpose.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_transpose.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavfilter/vf_transpose.o libavfilter/vf_transpose.o: \ + libavfilter/vf_transpose.c libavutil/intreadwrite.h libavutil/avconfig.h \ + libavutil/attributes.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/opt.h libavutil/rational.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/pixdesc.h libavutil/imgutils.h libavutil/pixdesc.h \ + libavutil/internal.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/formats.h \ + libavfilter/internal.h libavfilter/avfiltergraph.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_transpose.o Binary file ffmpeg/libavfilter/vf_transpose.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_unsharp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_unsharp.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,304 @@ +/* + * Original copyright (c) 2002 Remi Guyomarch + * Port copyright (c) 2010 Daniel G. Taylor + * Relicensed to the LGPL with permission from Remi Guyomarch. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * blur / sharpen filter, ported to FFmpeg from MPlayer + * libmpcodecs/unsharp.c. + * + * This code is based on: + * + * An Efficient algorithm for Gaussian blur using finite-state machines + * Frederick M. Waltz and John W. V. Miller + * + * SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII + * Originally published Boston, Nov 98 + * + * http://www.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf + */ + +#include /* DBL_MAX */ + +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" +#include "libavutil/common.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +#define MIN_MATRIX_SIZE 3 +#define MAX_MATRIX_SIZE 63 + +/* right-shift and round-up */ +#define SHIFTUP(x,shift) (-((-(x))>>(shift))) + +typedef struct FilterParam { + int msize_x; ///< matrix width + int msize_y; ///< matrix height + int amount; ///< effect amount + int steps_x; ///< horizontal step count + int steps_y; ///< vertical step count + int scalebits; ///< bits to shift pixel + int32_t halfscale; ///< amount to add to pixel + uint32_t *sc[MAX_MATRIX_SIZE - 1]; ///< finite state machine storage +} FilterParam; + +typedef struct { + const AVClass *class; + int lmsize_x, lmsize_y, cmsize_x, cmsize_y; + float lamount, camount; + FilterParam luma; ///< luma parameters (width, height, amount) + FilterParam chroma; ///< chroma parameters (width, height, amount) + int hsub, vsub; +} UnsharpContext; + +static void apply_unsharp( uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, + int width, int height, FilterParam *fp) +{ + uint32_t **sc = fp->sc; + uint32_t sr[MAX_MATRIX_SIZE - 1], tmp1, tmp2; + + int32_t res; + int x, y, z; + const uint8_t *src2 = NULL; //silence a warning + const int amount = fp->amount; + const int steps_x = fp->steps_x; + const int steps_y = fp->steps_y; + const int scalebits = fp->scalebits; + const int32_t halfscale = fp->halfscale; + + if (!amount) { + if (dst_stride == src_stride) + memcpy(dst, src, src_stride * height); + else + for (y = 0; y < height; y++, dst += dst_stride, src += src_stride) + memcpy(dst, src, width); + return; + } + + for (y = 0; y < 2 * steps_y; y++) + memset(sc[y], 0, sizeof(sc[y][0]) * (width + 2 * steps_x)); + + for (y = -steps_y; y < height + steps_y; y++) { + if (y < height) + src2 = src; + + memset(sr, 0, sizeof(sr[0]) * (2 * steps_x - 1)); + for (x = -steps_x; x < width + steps_x; x++) { + tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; + for (z = 0; z < steps_x * 2; z += 2) { + tmp2 = sr[z + 0] + tmp1; sr[z + 0] = tmp1; + tmp1 = sr[z + 1] + tmp2; sr[z + 1] = tmp2; + } + for (z = 0; z < steps_y * 2; z += 2) { + tmp2 = sc[z + 0][x + steps_x] + tmp1; sc[z + 0][x + steps_x] = tmp1; + tmp1 = sc[z + 1][x + steps_x] + tmp2; sc[z + 1][x + steps_x] = tmp2; + } + if (x >= steps_x && y >= steps_y) { + const uint8_t *srx = src - steps_y * src_stride + x - steps_x; + uint8_t *dsx = dst - steps_y * dst_stride + x - steps_x; + + res = (int32_t)*srx + ((((int32_t) * srx - (int32_t)((tmp1 + halfscale) >> scalebits)) * amount) >> 16); + *dsx = av_clip_uint8(res); + } + } + if (y >= 0) { + dst += dst_stride; + src += src_stride; + } + } +} + +static void set_filter_param(FilterParam *fp, int msize_x, int msize_y, float amount) +{ + fp->msize_x = msize_x; + fp->msize_y = msize_y; + fp->amount = amount * 65536.0; + + fp->steps_x = msize_x / 2; + fp->steps_y = msize_y / 2; + fp->scalebits = (fp->steps_x + fp->steps_y) * 2; + fp->halfscale = 1 << (fp->scalebits - 1); +} + +static av_cold int init(AVFilterContext *ctx) +{ + UnsharpContext *unsharp = ctx->priv; + + + set_filter_param(&unsharp->luma, unsharp->lmsize_x, unsharp->lmsize_y, unsharp->lamount); + set_filter_param(&unsharp->chroma, unsharp->cmsize_x, unsharp->cmsize_y, unsharp->camount); + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + + return 0; +} + +static int init_filter_param(AVFilterContext *ctx, FilterParam *fp, const char *effect_type, int width) +{ + int z; + const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen"; + + if (!(fp->msize_x & fp->msize_y & 1)) { + av_log(ctx, AV_LOG_ERROR, + "Invalid even size for %s matrix size %dx%d\n", + effect_type, fp->msize_x, fp->msize_y); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", + effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0); + + for (z = 0; z < 2 * fp->steps_y; z++) + if (!(fp->sc[z] = av_malloc(sizeof(*(fp->sc[z])) * (width + 2 * fp->steps_x)))) + return AVERROR(ENOMEM); + + return 0; +} + +static int config_props(AVFilterLink *link) +{ + UnsharpContext *unsharp = link->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); + int ret; + + unsharp->hsub = desc->log2_chroma_w; + unsharp->vsub = desc->log2_chroma_h; + + ret = init_filter_param(link->dst, &unsharp->luma, "luma", link->w); + if (ret < 0) + return ret; + ret = init_filter_param(link->dst, &unsharp->chroma, "chroma", SHIFTUP(link->w, unsharp->hsub)); + if (ret < 0) + return ret; + + return 0; +} + +static void free_filter_param(FilterParam *fp) +{ + int z; + + for (z = 0; z < 2 * fp->steps_y; z++) + av_free(fp->sc[z]); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + UnsharpContext *unsharp = ctx->priv; + + free_filter_param(&unsharp->luma); + free_filter_param(&unsharp->chroma); +} + +static int filter_frame(AVFilterLink *link, AVFrame *in) +{ + UnsharpContext *unsharp = link->dst->priv; + AVFilterLink *outlink = link->dst->outputs[0]; + AVFrame *out; + int cw = SHIFTUP(link->w, unsharp->hsub); + int ch = SHIFTUP(link->h, unsharp->vsub); + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + apply_unsharp(out->data[0], out->linesize[0], in->data[0], in->linesize[0], link->w, link->h, &unsharp->luma); + apply_unsharp(out->data[1], out->linesize[1], in->data[1], in->linesize[1], cw, ch, &unsharp->chroma); + apply_unsharp(out->data[2], out->linesize[2], in->data[2], in->linesize[2], cw, ch, &unsharp->chroma); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +#define OFFSET(x) offsetof(UnsharpContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define MIN_SIZE 3 +#define MAX_SIZE 63 +static const AVOption unsharp_options[] = { + { "luma_msize_x", "luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "lx", "luma matrix horizontal size", OFFSET(lmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "luma_msize_y", "luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "ly", "luma matrix vertical size", OFFSET(lmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "luma_amount", "luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS }, + { "la", "luma effect strength", OFFSET(lamount), AV_OPT_TYPE_FLOAT, { .dbl = 1 }, -2, 5, FLAGS }, + { "chroma_msize_x", "chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "cx", "chroma matrix horizontal size", OFFSET(cmsize_x), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "chroma_msize_y", "chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "cy", "chroma matrix vertical size", OFFSET(cmsize_y), AV_OPT_TYPE_INT, { .i64 = 5 }, MIN_SIZE, MAX_SIZE, FLAGS }, + { "chroma_amount", "chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS }, + { "ca", "chroma effect strength", OFFSET(camount), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, -2, 5, FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(unsharp); + +static const AVFilterPad avfilter_vf_unsharp_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_props, + }, + { NULL } +}; + +static const AVFilterPad avfilter_vf_unsharp_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter avfilter_vf_unsharp = { + .name = "unsharp", + .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."), + + .priv_size = sizeof(UnsharpContext), + .priv_class = &unsharp_class, + + .init = init, + .uninit = uninit, + .query_formats = query_formats, + + .inputs = avfilter_vf_unsharp_inputs, + + .outputs = avfilter_vf_unsharp_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_unsharp.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_unsharp.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,17 @@ +libavfilter/vf_unsharp.o libavfilter/vf_unsharp.o: \ + libavfilter/vf_unsharp.c libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/formats.h \ + libavfilter/internal.h libavfilter/avfiltergraph.h libavfilter/video.h \ + libavutil/common.h libavutil/mem.h libavutil/opt.h libavutil/pixdesc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_unsharp.o Binary file ffmpeg/libavfilter/vf_unsharp.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_vflip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_vflip.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2007 Bobby Bingham + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * video vertical flip filter + */ + +#include "libavutil/internal.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "internal.h" +#include "video.h" + +typedef struct { + int vsub; ///< vertical chroma subsampling +} FlipContext; + +static int config_input(AVFilterLink *link) +{ + FlipContext *flip = link->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); + + flip->vsub = desc->log2_chroma_h; + + return 0; +} + +static AVFrame *get_video_buffer(AVFilterLink *link, int w, int h) +{ + FlipContext *flip = link->dst->priv; + AVFrame *frame; + int i; + + frame = ff_get_video_buffer(link->dst->outputs[0], w, h); + if (!frame) + return NULL; + + for (i = 0; i < 4; i ++) { + int vsub = i == 1 || i == 2 ? flip->vsub : 0; + + if (frame->data[i]) { + frame->data[i] += (((h + (1<> vsub) - 1) * frame->linesize[i]; + frame->linesize[i] = -frame->linesize[i]; + } + } + + return frame; +} + +static int filter_frame(AVFilterLink *link, AVFrame *frame) +{ + FlipContext *flip = link->dst->priv; + int i; + + for (i = 0; i < 4; i ++) { + int vsub = i == 1 || i == 2 ? flip->vsub : 0; + + if (frame->data[i]) { + frame->data[i] += (((link->h + (1<> vsub)-1) * frame->linesize[i]; + frame->linesize[i] = -frame->linesize[i]; + } + } + + return ff_filter_frame(link->dst->outputs[0], frame); +} +static const AVFilterPad avfilter_vf_vflip_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = get_video_buffer, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad avfilter_vf_vflip_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter avfilter_vf_vflip = { + .name = "vflip", + .description = NULL_IF_CONFIG_SMALL("Flip the input video vertically."), + + .priv_size = sizeof(FlipContext), + + .inputs = avfilter_vf_vflip_inputs, + .outputs = avfilter_vf_vflip_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_vflip.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_vflip.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,16 @@ +libavfilter/vf_vflip.o libavfilter/vf_vflip.o: libavfilter/vf_vflip.c \ + libavutil/internal.h config.h libavutil/attributes.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/pixdesc.h \ + libavutil/pixfmt.h libavutil/avconfig.h libavutil/version.h \ + libavutil/old_pix_fmts.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/internal.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/formats.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_vflip.o Binary file ffmpeg/libavfilter/vf_vflip.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vf_yadif.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vf_yadif.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2006-2011 Michael Niedermayer + * 2010 James Darnley + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "libavutil/avassert.h" +#include "libavutil/cpu.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" +#include "yadif.h" + +#undef NDEBUG +#include + +#define CHECK(j)\ + { int score = FFABS(cur[mrefs - 1 + (j)] - cur[prefs - 1 - (j)])\ + + FFABS(cur[mrefs +(j)] - cur[prefs -(j)])\ + + FFABS(cur[mrefs + 1 + (j)] - cur[prefs + 1 - (j)]);\ + if (score < spatial_score) {\ + spatial_score= score;\ + spatial_pred= (cur[mrefs +(j)] + cur[prefs -(j)])>>1;\ + +/* The is_not_edge argument here controls when the code will enter a branch + * which reads up to and including x-3 and x+3. */ + +#define FILTER(start, end, is_not_edge) \ + for (x = start; x < end; x++) { \ + int c = cur[mrefs]; \ + int d = (prev2[0] + next2[0])>>1; \ + int e = cur[prefs]; \ + int temporal_diff0 = FFABS(prev2[0] - next2[0]); \ + int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e) )>>1; \ + int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e) )>>1; \ + int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \ + int spatial_pred = (c+e) >> 1; \ + \ + if (is_not_edge) {\ + int spatial_score = FFABS(cur[mrefs - 1] - cur[prefs - 1]) + FFABS(c-e) \ + + FFABS(cur[mrefs + 1] - cur[prefs + 1]) - 1; \ + CHECK(-1) CHECK(-2) }} }} \ + CHECK( 1) CHECK( 2) }} }} \ + }\ + \ + if (mode < 2) { \ + int b = (prev2[2 * mrefs] + next2[2 * mrefs])>>1; \ + int f = (prev2[2 * prefs] + next2[2 * prefs])>>1; \ + int max = FFMAX3(d - e, d - c, FFMIN(b - c, f - e)); \ + int min = FFMIN3(d - e, d - c, FFMAX(b - c, f - e)); \ + \ + diff = FFMAX3(diff, min, -max); \ + } \ + \ + if (spatial_pred > d + diff) \ + spatial_pred = d + diff; \ + else if (spatial_pred < d - diff) \ + spatial_pred = d - diff; \ + \ + dst[0] = spatial_pred; \ + \ + dst++; \ + cur++; \ + prev++; \ + next++; \ + prev2++; \ + next2++; \ + } + +static void filter_line_c(void *dst1, + void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int parity, int mode) +{ + uint8_t *dst = dst1; + uint8_t *prev = prev1; + uint8_t *cur = cur1; + uint8_t *next = next1; + int x; + uint8_t *prev2 = parity ? prev : cur ; + uint8_t *next2 = parity ? cur : next; + + /* The function is called with the pointers already pointing to data[3] and + * with 6 subtracted from the width. This allows the FILTER macro to be + * called so that it processes all the pixels normally. A constant value of + * true for is_not_edge lets the compiler ignore the if statement. */ + FILTER(0, w, 1) +} + +static void filter_edges(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int parity, int mode) +{ + uint8_t *dst = dst1; + uint8_t *prev = prev1; + uint8_t *cur = cur1; + uint8_t *next = next1; + int x; + uint8_t *prev2 = parity ? prev : cur ; + uint8_t *next2 = parity ? cur : next; + + /* Only edge pixels need to be processed here. A constant value of false + * for is_not_edge should let the compiler ignore the whole branch. */ + FILTER(0, 3, 0) + + dst = (uint8_t*)dst1 + w - 3; + prev = (uint8_t*)prev1 + w - 3; + cur = (uint8_t*)cur1 + w - 3; + next = (uint8_t*)next1 + w - 3; + prev2 = (uint8_t*)(parity ? prev : cur); + next2 = (uint8_t*)(parity ? cur : next); + + FILTER(w - 3, w, 0) +} + + +static void filter_line_c_16bit(void *dst1, + void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int parity, + int mode) +{ + uint16_t *dst = dst1; + uint16_t *prev = prev1; + uint16_t *cur = cur1; + uint16_t *next = next1; + int x; + uint16_t *prev2 = parity ? prev : cur ; + uint16_t *next2 = parity ? cur : next; + mrefs /= 2; + prefs /= 2; + + FILTER(0, w, 1) +} + +static void filter_edges_16bit(void *dst1, void *prev1, void *cur1, void *next1, + int w, int prefs, int mrefs, int parity, int mode) +{ + uint16_t *dst = dst1; + uint16_t *prev = prev1; + uint16_t *cur = cur1; + uint16_t *next = next1; + int x; + uint16_t *prev2 = parity ? prev : cur ; + uint16_t *next2 = parity ? cur : next; + mrefs /= 2; + prefs /= 2; + + FILTER(0, 3, 0) + + dst = (uint16_t*)dst1 + w - 3; + prev = (uint16_t*)prev1 + w - 3; + cur = (uint16_t*)cur1 + w - 3; + next = (uint16_t*)next1 + w - 3; + prev2 = (uint16_t*)(parity ? prev : cur); + next2 = (uint16_t*)(parity ? cur : next); + + FILTER(w - 3, w, 0) +} + +static void filter(AVFilterContext *ctx, AVFrame *dstpic, + int parity, int tff) +{ + YADIFContext *yadif = ctx->priv; + int y, i; + + for (i = 0; i < yadif->csp->nb_components; i++) { + int w = dstpic->width; + int h = dstpic->height; + int refs = yadif->cur->linesize[i]; + int df = (yadif->csp->comp[i].depth_minus1 + 8) / 8; + int pix_3 = 3 * df; + + if (i == 1 || i == 2) { + /* Why is this not part of the per-plane description thing? */ + w >>= yadif->csp->log2_chroma_w; + h >>= yadif->csp->log2_chroma_h; + } + + /* filtering reads 3 pixels to the left/right; to avoid invalid reads, + * we need to call the c variant which avoids this for border pixels + */ + + for (y = 0; y < h; y++) { + if ((y ^ parity) & 1) { + uint8_t *prev = &yadif->prev->data[i][y * refs]; + uint8_t *cur = &yadif->cur ->data[i][y * refs]; + uint8_t *next = &yadif->next->data[i][y * refs]; + uint8_t *dst = &dstpic->data[i][y * dstpic->linesize[i]]; + int mode = y == 1 || y + 2 == h ? 2 : yadif->mode; + yadif->filter_line(dst + pix_3, prev + pix_3, cur + pix_3, + next + pix_3, w - 6, + y + 1 < h ? refs : -refs, + y ? -refs : refs, + parity ^ tff, mode); + yadif->filter_edges(dst, prev, cur, next, w, + y + 1 < h ? refs : -refs, + y ? -refs : refs, + parity ^ tff, mode); + } else { + memcpy(&dstpic->data[i][y * dstpic->linesize[i]], + &yadif->cur->data[i][y * refs], w * df); + } + } + } + + emms_c(); +} + +static int return_frame(AVFilterContext *ctx, int is_second) +{ + YADIFContext *yadif = ctx->priv; + AVFilterLink *link = ctx->outputs[0]; + int tff, ret; + + if (yadif->parity == -1) { + tff = yadif->cur->interlaced_frame ? + yadif->cur->top_field_first : 1; + } else { + tff = yadif->parity ^ 1; + } + + if (is_second) { + yadif->out = ff_get_video_buffer(link, link->w, link->h); + if (!yadif->out) + return AVERROR(ENOMEM); + + av_frame_copy_props(yadif->out, yadif->cur); + yadif->out->interlaced_frame = 0; + } + + filter(ctx, yadif->out, tff ^ !is_second, tff); + + if (is_second) { + int64_t cur_pts = yadif->cur->pts; + int64_t next_pts = yadif->next->pts; + + if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { + yadif->out->pts = cur_pts + next_pts; + } else { + yadif->out->pts = AV_NOPTS_VALUE; + } + } + ret = ff_filter_frame(ctx->outputs[0], yadif->out); + + yadif->frame_pending = (yadif->mode&1) && !is_second; + return ret; +} + +static int filter_frame(AVFilterLink *link, AVFrame *frame) +{ + AVFilterContext *ctx = link->dst; + YADIFContext *yadif = ctx->priv; + + av_assert0(frame); + + if (yadif->frame_pending) + return_frame(ctx, 1); + + if (yadif->prev) + av_frame_free(&yadif->prev); + yadif->prev = yadif->cur; + yadif->cur = yadif->next; + yadif->next = frame; + + if (!yadif->cur) + return 0; + + if (yadif->deint && !yadif->cur->interlaced_frame) { + yadif->out = av_frame_clone(yadif->cur); + if (!yadif->out) + return AVERROR(ENOMEM); + + av_frame_free(&yadif->prev); + if (yadif->out->pts != AV_NOPTS_VALUE) + yadif->out->pts *= 2; + return ff_filter_frame(ctx->outputs[0], yadif->out); + } + + if (!yadif->prev && + !(yadif->prev = av_frame_clone(yadif->cur))) + return AVERROR(ENOMEM); + + yadif->out = ff_get_video_buffer(ctx->outputs[0], link->w, link->h); + if (!yadif->out) + return AVERROR(ENOMEM); + + av_frame_copy_props(yadif->out, yadif->cur); + yadif->out->interlaced_frame = 0; + + if (yadif->out->pts != AV_NOPTS_VALUE) + yadif->out->pts *= 2; + + return return_frame(ctx, 0); +} + +static int request_frame(AVFilterLink *link) +{ + AVFilterContext *ctx = link->src; + YADIFContext *yadif = ctx->priv; + + if (yadif->frame_pending) { + return_frame(ctx, 1); + return 0; + } + + do { + int ret; + + if (yadif->eof) + return AVERROR_EOF; + + ret = ff_request_frame(link->src->inputs[0]); + + if (ret == AVERROR_EOF && yadif->cur) { + AVFrame *next = av_frame_clone(yadif->next); + + if (!next) + return AVERROR(ENOMEM); + + next->pts = yadif->next->pts * 2 - yadif->cur->pts; + + filter_frame(link->src->inputs[0], next); + yadif->eof = 1; + } else if (ret < 0) { + return ret; + } + } while (!yadif->cur); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + YADIFContext *yadif = ctx->priv; + + av_frame_free(&yadif->prev); + av_frame_free(&yadif->cur ); + av_frame_free(&yadif->next); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUVJ444P, + AV_NE( AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE ), + AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ440P, + AV_NE( AV_PIX_FMT_YUV420P9BE, AV_PIX_FMT_YUV420P9LE ), + AV_NE( AV_PIX_FMT_YUV422P9BE, AV_PIX_FMT_YUV422P9LE ), + AV_NE( AV_PIX_FMT_YUV444P9BE, AV_PIX_FMT_YUV444P9LE ), + AV_NE( AV_PIX_FMT_YUV420P10BE, AV_PIX_FMT_YUV420P10LE ), + AV_NE( AV_PIX_FMT_YUV422P10BE, AV_PIX_FMT_YUV422P10LE ), + AV_NE( AV_PIX_FMT_YUV444P10BE, AV_PIX_FMT_YUV444P10LE ), + AV_NE( AV_PIX_FMT_YUV420P12BE, AV_PIX_FMT_YUV420P12LE ), + AV_NE( AV_PIX_FMT_YUV422P12BE, AV_PIX_FMT_YUV422P12LE ), + AV_NE( AV_PIX_FMT_YUV444P12BE, AV_PIX_FMT_YUV444P12LE ), + AV_NE( AV_PIX_FMT_YUV420P14BE, AV_PIX_FMT_YUV420P14LE ), + AV_NE( AV_PIX_FMT_YUV422P14BE, AV_PIX_FMT_YUV422P14LE ), + AV_NE( AV_PIX_FMT_YUV444P14BE, AV_PIX_FMT_YUV444P14LE ), + AV_NE( AV_PIX_FMT_YUV420P16BE, AV_PIX_FMT_YUV420P16LE ), + AV_NE( AV_PIX_FMT_YUV422P16BE, AV_PIX_FMT_YUV422P16LE ), + AV_NE( AV_PIX_FMT_YUV444P16BE, AV_PIX_FMT_YUV444P16LE ), + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUVA422P, + AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + + return 0; +} + +static int config_props(AVFilterLink *link) +{ + AVFilterContext *ctx = link->src; + YADIFContext *s = link->src->priv; + + link->time_base.num = link->src->inputs[0]->time_base.num; + link->time_base.den = link->src->inputs[0]->time_base.den * 2; + link->w = link->src->inputs[0]->w; + link->h = link->src->inputs[0]->h; + + if(s->mode&1) + link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1}); + + if (link->w < 3 || link->h < 3) { + av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); + return AVERROR(EINVAL); + } + + s->csp = av_pix_fmt_desc_get(link->format); + if (s->csp->comp[0].depth_minus1 / 8 == 1) { + s->filter_line = filter_line_c_16bit; + s->filter_edges = filter_edges_16bit; + } else { + s->filter_line = filter_line_c; + s->filter_edges = filter_edges; + } + + if (ARCH_X86) + ff_yadif_init_x86(s); + + return 0; +} + + +#define OFFSET(x) offsetof(YADIFContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit } + +static const AVOption yadif_options[] = { + { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, "mode"}, + CONST("send_frame", "send one frame for each frame", YADIF_MODE_SEND_FRAME, "mode"), + CONST("send_field", "send one frame for each field", YADIF_MODE_SEND_FIELD, "mode"), + CONST("send_frame_nospatial", "send one frame for each frame, but skip spatial interlacing check", YADIF_MODE_SEND_FRAME_NOSPATIAL, "mode"), + CONST("send_field_nospatial", "send one frame for each field, but skip spatial interlacing check", YADIF_MODE_SEND_FIELD_NOSPATIAL, "mode"), + + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, "parity" }, + CONST("tff", "assume top field first", YADIF_PARITY_TFF, "parity"), + CONST("bff", "assume bottom field first", YADIF_PARITY_BFF, "parity"), + CONST("auto", "auto detect parity", YADIF_PARITY_AUTO, "parity"), + + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, "deint" }, + CONST("all", "deinterlace all frames", YADIF_DEINT_ALL, "deint"), + CONST("interlaced", "only deinterlace frames marked as interlaced", YADIF_DEINT_INTERLACED, "deint"), + + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(yadif); + +static const AVFilterPad avfilter_vf_yadif_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad avfilter_vf_yadif_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL } +}; + +AVFilter avfilter_vf_yadif = { + .name = "yadif", + .description = NULL_IF_CONFIG_SMALL("Deinterlace the input image."), + + .priv_size = sizeof(YADIFContext), + .priv_class = &yadif_class, + .uninit = uninit, + .query_formats = query_formats, + + .inputs = avfilter_vf_yadif_inputs, + .outputs = avfilter_vf_yadif_outputs, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/video.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/video.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,157 @@ +/* + * Copyright 2007 Bobby Bingham + * Copyright Stefano Sabatini + * Copyright Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/buffer.h" +#include "libavutil/imgutils.h" +#include "libavutil/mem.h" + +#include "avfilter.h" +#include "internal.h" +#include "video.h" + +AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h) +{ + return ff_get_video_buffer(link->dst->outputs[0], w, h); +} + +/* TODO: set the buffer's priv member to a context structure for the whole + * filter chain. This will allow for a buffer pool instead of the constant + * alloc & free cycle currently implemented. */ +AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h) +{ + AVFrame *frame = av_frame_alloc(); + int ret; + +#if 0 //POOL + AVFilterPool *pool = link->pool; + if (pool) { + for (i = 0; i < POOL_SIZE; i++) { + picref = pool->pic[i]; + if (picref && picref->buf->format == link->format && picref->buf->w == w && picref->buf->h == h) { + AVFilterBuffer *pic = picref->buf; + pool->pic[i] = NULL; + pool->count--; + av_assert0(!picref->video->qp_table); + picref->video->w = w; + picref->video->h = h; + picref->perms = full_perms; + picref->format = link->format; + pic->refcount = 1; + memcpy(picref->data, pic->data, sizeof(picref->data)); + memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); + pool->refcount++; + return picref; + } + } + } else { + pool = link->pool = av_mallocz(sizeof(AVFilterPool)); + pool->refcount = 1; + } +#endif + if (!frame) + return NULL; + + frame->width = w; + frame->height = h; + frame->format = link->format; + + ret = av_frame_get_buffer(frame, 32); + if (ret < 0) + av_frame_free(&frame); + +#if 0 //POOL + memset(data[0], 128, i); + + picref->buf->priv = pool; + picref->buf->free = NULL; + pool->refcount++; +#endif + + return frame; +} + +#if FF_API_AVFILTERBUFFER +AVFilterBufferRef * +avfilter_get_video_buffer_ref_from_arrays(uint8_t * const data[4], const int linesize[4], int perms, + int w, int h, enum AVPixelFormat format) +{ + AVFilterBuffer *pic = av_mallocz(sizeof(AVFilterBuffer)); + AVFilterBufferRef *picref = av_mallocz(sizeof(AVFilterBufferRef)); + + if (!pic || !picref) + goto fail; + + picref->buf = pic; + picref->buf->free = ff_avfilter_default_free_buffer; + if (!(picref->video = av_mallocz(sizeof(AVFilterBufferRefVideoProps)))) + goto fail; + + pic->w = picref->video->w = w; + pic->h = picref->video->h = h; + + /* make sure the buffer gets read permission or it's useless for output */ + picref->perms = perms | AV_PERM_READ; + + pic->refcount = 1; + picref->type = AVMEDIA_TYPE_VIDEO; + pic->format = picref->format = format; + + memcpy(pic->data, data, 4*sizeof(data[0])); + memcpy(pic->linesize, linesize, 4*sizeof(linesize[0])); + memcpy(picref->data, pic->data, sizeof(picref->data)); + memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); + + pic-> extended_data = pic->data; + picref->extended_data = picref->data; + + picref->pts = AV_NOPTS_VALUE; + + return picref; + +fail: + if (picref && picref->video) + av_free(picref->video); + av_free(picref); + av_free(pic); + return NULL; +} +#endif + +AVFrame *ff_get_video_buffer(AVFilterLink *link, int w, int h) +{ + AVFrame *ret = NULL; + + av_unused char buf[16]; + FF_TPRINTF_START(NULL, get_video_buffer); ff_tlog_link(NULL, link, 0); + + if (link->dstpad->get_video_buffer) + ret = link->dstpad->get_video_buffer(link, w, h); + + if (!ret) + ret = ff_default_get_video_buffer(link, w, h); + + return ret; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/video.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/video.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,17 @@ +libavfilter/video.o libavfilter/video.o: libavfilter/video.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/buffer.h \ + libavutil/imgutils.h libavutil/pixdesc.h libavutil/mem.h \ + libavfilter/avfilter.h libavutil/avutil.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/formats.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/video.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/video.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2007 Bobby Bingham + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_VIDEO_H +#define AVFILTER_VIDEO_H + +#include "avfilter.h" + +AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h); +AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h); + +/** + * Request a picture buffer with a specific set of permissions. + * + * @param link the output link to the filter from which the buffer will + * be requested + * @param w the minimum width of the buffer to allocate + * @param h the minimum height of the buffer to allocate + * @return A reference to the buffer. This must be unreferenced with + * avfilter_unref_buffer when you are finished with it. + */ +AVFrame *ff_get_video_buffer(AVFilterLink *link, int w, int h); + +#endif /* AVFILTER_VIDEO_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/video.o Binary file ffmpeg/libavfilter/video.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsink_nullsink.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsink_nullsink.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,46 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avfilter.h" +#include "internal.h" +#include "libavutil/internal.h" + +static int filter_frame(AVFilterLink *link, AVFrame *frame) +{ + av_frame_free(&frame); + return 0; +} + +static const AVFilterPad avfilter_vsink_nullsink_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL }, +}; + +AVFilter avfilter_vsink_nullsink = { + .name = "nullsink", + .description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input video."), + + .priv_size = 0, + + .inputs = avfilter_vsink_nullsink_inputs, + .outputs = NULL, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsink_nullsink.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsink_nullsink.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,17 @@ +libavfilter/vsink_nullsink.o libavfilter/vsink_nullsink.o: \ + libavfilter/vsink_nullsink.c libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/formats.h libavfilter/video.h \ + libavutil/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsink_nullsink.o Binary file ffmpeg/libavfilter/vsink_nullsink.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_cellauto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_cellauto.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,337 @@ +/* + * Copyright (c) Stefano Sabatini 2011 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * cellular automaton video source, based on Stephen Wolfram "experimentus crucis" + */ + +/* #define DEBUG */ + +#include "libavutil/file.h" +#include "libavutil/lfg.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/random_seed.h" +#include "libavutil/avstring.h" +#include "avfilter.h" +#include "internal.h" +#include "formats.h" +#include "video.h" + +typedef struct { + const AVClass *class; + int w, h; + char *filename; + char *rule_str; + uint8_t *file_buf; + size_t file_bufsize; + uint8_t *buf; + int buf_prev_row_idx, buf_row_idx; + uint8_t rule; + uint64_t pts; + AVRational frame_rate; + double random_fill_ratio; + uint32_t random_seed; + int stitch, scroll, start_full; + int64_t generation; ///< the generation number, starting from 0 + AVLFG lfg; + char *pattern; +} CellAutoContext; + +#define OFFSET(x) offsetof(CellAutoContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption cellauto_options[] = { + { "filename", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "f", "read initial pattern from file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "pattern", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "p", "set initial pattern", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS }, + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS }, + { "rule", "set rule", OFFSET(rule), AV_OPT_TYPE_INT, {.i64 = 110}, 0, 255, FLAGS }, + { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1, FLAGS }, + { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1, FLAGS }, + { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, + { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, + { "scroll", "scroll pattern downward", OFFSET(scroll), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, FLAGS }, + { "start_full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS }, + { "full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, FLAGS }, + { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(cellauto); + +#ifdef DEBUG +static void show_cellauto_row(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + int i; + uint8_t *row = cellauto->buf + cellauto->w * cellauto->buf_row_idx; + char *line = av_malloc(cellauto->w + 1); + if (!line) + return; + + for (i = 0; i < cellauto->w; i++) + line[i] = row[i] ? '@' : ' '; + line[i] = 0; + av_log(ctx, AV_LOG_DEBUG, "generation:%"PRId64" row:%s|\n", cellauto->generation, line); + av_free(line); +} +#endif + +static int init_pattern_from_string(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + char *p; + int i, w = 0; + + w = strlen(cellauto->pattern); + av_log(ctx, AV_LOG_DEBUG, "w:%d\n", w); + + if (cellauto->w) { + if (w > cellauto->w) { + av_log(ctx, AV_LOG_ERROR, + "The specified width is %d which cannot contain the provided string width of %d\n", + cellauto->w, w); + return AVERROR(EINVAL); + } + } else { + /* width was not specified, set it to width of the provided row */ + cellauto->w = w; + cellauto->h = (double)cellauto->w * M_PHI; + } + + cellauto->buf = av_mallocz(sizeof(uint8_t) * cellauto->w * cellauto->h); + if (!cellauto->buf) + return AVERROR(ENOMEM); + + /* fill buf */ + p = cellauto->pattern; + for (i = (cellauto->w - w)/2;; i++) { + av_log(ctx, AV_LOG_DEBUG, "%d %c\n", i, *p == '\n' ? 'N' : *p); + if (*p == '\n' || !*p) + break; + else + cellauto->buf[i] = !!av_isgraph(*(p++)); + } + + return 0; +} + +static int init_pattern_from_file(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + int ret; + + ret = av_file_map(cellauto->filename, + &cellauto->file_buf, &cellauto->file_bufsize, 0, ctx); + if (ret < 0) + return ret; + + /* create a string based on the read file */ + cellauto->pattern = av_malloc(cellauto->file_bufsize + 1); + if (!cellauto->pattern) + return AVERROR(ENOMEM); + memcpy(cellauto->pattern, cellauto->file_buf, cellauto->file_bufsize); + cellauto->pattern[cellauto->file_bufsize] = 0; + + return init_pattern_from_string(ctx); +} + +static int init(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + int ret; + + if (!cellauto->w && !cellauto->filename && !cellauto->pattern) + av_opt_set(cellauto, "size", "320x518", 0); + + if (cellauto->filename && cellauto->pattern) { + av_log(ctx, AV_LOG_ERROR, "Only one of the filename or pattern options can be used\n"); + return AVERROR(EINVAL); + } + + if (cellauto->filename) { + if ((ret = init_pattern_from_file(ctx)) < 0) + return ret; + } else if (cellauto->pattern) { + if ((ret = init_pattern_from_string(ctx)) < 0) + return ret; + } else { + /* fill the first row randomly */ + int i; + + cellauto->buf = av_mallocz(sizeof(uint8_t) * cellauto->w * cellauto->h); + if (!cellauto->buf) + return AVERROR(ENOMEM); + if (cellauto->random_seed == -1) + cellauto->random_seed = av_get_random_seed(); + + av_lfg_init(&cellauto->lfg, cellauto->random_seed); + + for (i = 0; i < cellauto->w; i++) { + double r = (double)av_lfg_get(&cellauto->lfg) / UINT32_MAX; + if (r <= cellauto->random_fill_ratio) + cellauto->buf[i] = 1; + } + } + + av_log(ctx, AV_LOG_VERBOSE, + "s:%dx%d r:%d/%d rule:%d stitch:%d scroll:%d full:%d seed:%u\n", + cellauto->w, cellauto->h, cellauto->frame_rate.num, cellauto->frame_rate.den, + cellauto->rule, cellauto->stitch, cellauto->scroll, cellauto->start_full, + cellauto->random_seed); + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + + av_file_unmap(cellauto->file_buf, cellauto->file_bufsize); + av_freep(&cellauto->buf); + av_freep(&cellauto->pattern); +} + +static int config_props(AVFilterLink *outlink) +{ + CellAutoContext *cellauto = outlink->src->priv; + + outlink->w = cellauto->w; + outlink->h = cellauto->h; + outlink->time_base = av_inv_q(cellauto->frame_rate); + + return 0; +} + +static void evolve(AVFilterContext *ctx) +{ + CellAutoContext *cellauto = ctx->priv; + int i, v, pos[3]; + uint8_t *row, *prev_row = cellauto->buf + cellauto->buf_row_idx * cellauto->w; + enum { NW, N, NE }; + + cellauto->buf_prev_row_idx = cellauto->buf_row_idx; + cellauto->buf_row_idx = cellauto->buf_row_idx == cellauto->h-1 ? 0 : cellauto->buf_row_idx+1; + row = cellauto->buf + cellauto->w * cellauto->buf_row_idx; + + for (i = 0; i < cellauto->w; i++) { + if (cellauto->stitch) { + pos[NW] = i-1 < 0 ? cellauto->w-1 : i-1; + pos[N] = i; + pos[NE] = i+1 == cellauto->w ? 0 : i+1; + v = prev_row[pos[NW]]<<2 | prev_row[pos[N]]<<1 | prev_row[pos[NE]]; + } else { + v = 0; + v|= i-1 >= 0 ? prev_row[i-1]<<2 : 0; + v|= prev_row[i ]<<1 ; + v|= i+1 < cellauto->w ? prev_row[i+1] : 0; + } + row[i] = !!(cellauto->rule & (1< cell:%d\n", i, + v&4?'@':' ', v&2?'@':' ', v&1?'@':' ', row[i]); + } + + cellauto->generation++; +} + +static void fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + CellAutoContext *cellauto = ctx->priv; + int i, j, k, row_idx = 0; + uint8_t *p0 = picref->data[0]; + + if (cellauto->scroll && cellauto->generation >= cellauto->h) + /* show on top the oldest row */ + row_idx = (cellauto->buf_row_idx + 1) % cellauto->h; + + /* fill the output picture with the whole buffer */ + for (i = 0; i < cellauto->h; i++) { + uint8_t byte = 0; + uint8_t *row = cellauto->buf + row_idx*cellauto->w; + uint8_t *p = p0; + for (k = 0, j = 0; j < cellauto->w; j++) { + byte |= row[j]<<(7-k++); + if (k==8 || j == cellauto->w-1) { + k = 0; + *p++ = byte; + byte = 0; + } + } + row_idx = (row_idx + 1) % cellauto->h; + p0 += picref->linesize[0]; + } +} + +static int request_frame(AVFilterLink *outlink) +{ + CellAutoContext *cellauto = outlink->src->priv; + AVFrame *picref = ff_get_video_buffer(outlink, cellauto->w, cellauto->h); + if (!picref) + return AVERROR(ENOMEM); + picref->sample_aspect_ratio = (AVRational) {1, 1}; + if (cellauto->generation == 0 && cellauto->start_full) { + int i; + for (i = 0; i < cellauto->h-1; i++) + evolve(outlink->src); + } + fill_picture(outlink->src, picref); + evolve(outlink->src); + + picref->pts = cellauto->pts++; + +#ifdef DEBUG + show_cellauto_row(outlink->src); +#endif + return ff_filter_frame(outlink, picref); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_MONOBLACK, AV_PIX_FMT_NONE }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static const AVFilterPad cellauto_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL } +}; + +AVFilter avfilter_vsrc_cellauto = { + .name = "cellauto", + .description = NULL_IF_CONFIG_SMALL("Create pattern generated by an elementary cellular automaton."), + .priv_size = sizeof(CellAutoContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = NULL, + .outputs = cellauto_outputs, + .priv_class = &cellauto_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_cellauto.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_cellauto.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavfilter/vsrc_cellauto.o libavfilter/vsrc_cellauto.o: \ + libavfilter/vsrc_cellauto.c libavutil/file.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/lfg.h libavutil/opt.h libavutil/samplefmt.h \ + libavutil/parseutils.h libavutil/random_seed.h libavutil/avstring.h \ + libavfilter/avfilter.h libavutil/avutil.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/log.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavfilter/version.h \ + libavutil/avutil.h libavfilter/internal.h libavfilter/avfiltergraph.h \ + libavfilter/formats.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_cellauto.o Binary file ffmpeg/libavfilter/vsrc_cellauto.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_life.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_life.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,466 @@ +/* + * Copyright (c) Stefano Sabatini 2010 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * life video source, based on John Conways' Life Game + */ + +/* #define DEBUG */ + +#include "libavutil/file.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/lfg.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/random_seed.h" +#include "libavutil/avstring.h" +#include "avfilter.h" +#include "internal.h" +#include "formats.h" +#include "video.h" + +typedef struct { + const AVClass *class; + int w, h; + char *filename; + char *rule_str; + uint8_t *file_buf; + size_t file_bufsize; + + /** + * The two grid state buffers. + * + * A 0xFF (ALIVE_CELL) value means the cell is alive (or new born), while + * the decreasing values from 0xFE to 0 means the cell is dead; the range + * of values is used for the slow death effect, or mold (0xFE means dead, + * 0xFD means very dead, 0xFC means very very dead... and 0x00 means + * definitely dead/mold). + */ + uint8_t *buf[2]; + + uint8_t buf_idx; + uint16_t stay_rule; ///< encode the behavior for filled cells + uint16_t born_rule; ///< encode the behavior for empty cells + uint64_t pts; + AVRational frame_rate; + double random_fill_ratio; + uint32_t random_seed; + int stitch; + int mold; + char *life_color_str; + char *death_color_str; + char *mold_color_str; + uint8_t life_color[4]; + uint8_t death_color[4]; + uint8_t mold_color[4]; + AVLFG lfg; + void (*draw)(AVFilterContext*, AVFrame*); +} LifeContext; + +#define ALIVE_CELL 0xFF +#define OFFSET(x) offsetof(LifeContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption life_options[] = { + { "filename", "set source file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "f", "set source file", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS }, + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS }, + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "rule", "set rule", OFFSET(rule_str), AV_OPT_TYPE_STRING, {.str = "B3/S23"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS }, + { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS }, + { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS }, + { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS }, + { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS }, + { "mold", "set mold speed for dead cells", OFFSET(mold), AV_OPT_TYPE_INT, {.i64=0}, 0, 0xFF, FLAGS }, + { "life_color", "set life color", OFFSET( life_color_str), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "death_color", "set death color", OFFSET(death_color_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "mold_color", "set mold color", OFFSET( mold_color_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(life); + +static int parse_rule(uint16_t *born_rule, uint16_t *stay_rule, + const char *rule_str, void *log_ctx) +{ + char *tail; + const char *p = rule_str; + *born_rule = 0; + *stay_rule = 0; + + if (strchr("bBsS", *p)) { + /* parse rule as a Born / Stay Alive code, see + * http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life */ + do { + uint16_t *rule = (*p == 'b' || *p == 'B') ? born_rule : stay_rule; + p++; + while (*p >= '0' && *p <= '8') { + *rule += 1<<(*p - '0'); + p++; + } + if (*p != '/') + break; + p++; + } while (strchr("bBsS", *p)); + + if (*p) + goto error; + } else { + /* parse rule as a number, expressed in the form STAY|(BORN<<9), + * where STAY and BORN encode the corresponding 9-bits rule */ + long int rule = strtol(rule_str, &tail, 10); + if (*tail) + goto error; + *born_rule = ((1<<9)-1) & rule; + *stay_rule = rule >> 9; + } + + return 0; + +error: + av_log(log_ctx, AV_LOG_ERROR, "Invalid rule code '%s' provided\n", rule_str); + return AVERROR(EINVAL); +} + +#ifdef DEBUG +static void show_life_grid(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + int i, j; + + char *line = av_malloc(life->w + 1); + if (!line) + return; + for (i = 0; i < life->h; i++) { + for (j = 0; j < life->w; j++) + line[j] = life->buf[life->buf_idx][i*life->w + j] == ALIVE_CELL ? '@' : ' '; + line[j] = 0; + av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line); + } + av_free(line); +} +#endif + +static int init_pattern_from_file(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + char *p; + int ret, i, i0, j, h = 0, w, max_w = 0; + + if ((ret = av_file_map(life->filename, &life->file_buf, &life->file_bufsize, + 0, ctx)) < 0) + return ret; + av_freep(&life->filename); + + /* prescan file to get the number of lines and the maximum width */ + w = 0; + for (i = 0; i < life->file_bufsize; i++) { + if (life->file_buf[i] == '\n') { + h++; max_w = FFMAX(w, max_w); w = 0; + } else { + w++; + } + } + av_log(ctx, AV_LOG_DEBUG, "h:%d max_w:%d\n", h, max_w); + + if (life->w) { + if (max_w > life->w || h > life->h) { + av_log(ctx, AV_LOG_ERROR, + "The specified size is %dx%d which cannot contain the provided file size of %dx%d\n", + life->w, life->h, max_w, h); + return AVERROR(EINVAL); + } + } else { + /* size was not specified, set it to size of the grid */ + life->w = max_w; + life->h = h; + } + + if (!(life->buf[0] = av_mallocz(sizeof(char) * life->h * life->w)) || + !(life->buf[1] = av_mallocz(sizeof(char) * life->h * life->w))) { + av_free(life->buf[0]); + av_free(life->buf[1]); + return AVERROR(ENOMEM); + } + + /* fill buf[0] */ + p = life->file_buf; + for (i0 = 0, i = (life->h - h)/2; i0 < h; i0++, i++) { + for (j = (life->w - max_w)/2;; j++) { + av_log(ctx, AV_LOG_DEBUG, "%d:%d %c\n", i, j, *p == '\n' ? 'N' : *p); + if (*p == '\n') { + p++; break; + } else + life->buf[0][i*life->w + j] = av_isgraph(*(p++)) ? ALIVE_CELL : 0; + } + } + life->buf_idx = 0; + + return 0; +} + +static int init(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + int ret; + + if (!life->w && !life->filename) + av_opt_set(life, "size", "320x240", 0); + + if ((ret = parse_rule(&life->born_rule, &life->stay_rule, life->rule_str, ctx)) < 0) + return ret; + +#define PARSE_COLOR(name) do { \ + if ((ret = av_parse_color(life->name ## _color, life->name ## _color_str, -1, ctx))) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid " #name " color '%s'\n", \ + life->name ## _color_str); \ + return ret; \ + } \ + av_freep(&life->name ## _color_str); \ +} while (0) + + PARSE_COLOR(life); + PARSE_COLOR(death); + PARSE_COLOR(mold); + + if (!life->mold && memcmp(life->mold_color, "\x00\x00\x00", 3)) + av_log(ctx, AV_LOG_WARNING, + "Mold color is set while mold isn't, ignoring the color.\n"); + + if (!life->filename) { + /* fill the grid randomly */ + int i; + + if (!(life->buf[0] = av_mallocz(sizeof(char) * life->h * life->w)) || + !(life->buf[1] = av_mallocz(sizeof(char) * life->h * life->w))) { + av_free(life->buf[0]); + av_free(life->buf[1]); + return AVERROR(ENOMEM); + } + if (life->random_seed == -1) + life->random_seed = av_get_random_seed(); + + av_lfg_init(&life->lfg, life->random_seed); + + for (i = 0; i < life->w * life->h; i++) { + double r = (double)av_lfg_get(&life->lfg) / UINT32_MAX; + if (r <= life->random_fill_ratio) + life->buf[0][i] = ALIVE_CELL; + } + life->buf_idx = 0; + } else { + if ((ret = init_pattern_from_file(ctx)) < 0) + return ret; + } + + av_log(ctx, AV_LOG_VERBOSE, + "s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d seed:%u\n", + life->w, life->h, life->frame_rate.num, life->frame_rate.den, + life->rule_str, life->stay_rule, life->born_rule, life->stitch, + life->random_seed); + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + + av_file_unmap(life->file_buf, life->file_bufsize); + av_freep(&life->rule_str); + av_freep(&life->buf[0]); + av_freep(&life->buf[1]); +} + +static int config_props(AVFilterLink *outlink) +{ + LifeContext *life = outlink->src->priv; + + outlink->w = life->w; + outlink->h = life->h; + outlink->time_base = av_inv_q(life->frame_rate); + + return 0; +} + +static void evolve(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + int i, j; + uint8_t *oldbuf = life->buf[ life->buf_idx]; + uint8_t *newbuf = life->buf[!life->buf_idx]; + + enum { NW, N, NE, W, E, SW, S, SE }; + + /* evolve the grid */ + for (i = 0; i < life->h; i++) { + for (j = 0; j < life->w; j++) { + int pos[8][2], n, alive, cell; + if (life->stitch) { + pos[NW][0] = (i-1) < 0 ? life->h-1 : i-1; pos[NW][1] = (j-1) < 0 ? life->w-1 : j-1; + pos[N ][0] = (i-1) < 0 ? life->h-1 : i-1; pos[N ][1] = j ; + pos[NE][0] = (i-1) < 0 ? life->h-1 : i-1; pos[NE][1] = (j+1) == life->w ? 0 : j+1; + pos[W ][0] = i ; pos[W ][1] = (j-1) < 0 ? life->w-1 : j-1; + pos[E ][0] = i ; pos[E ][1] = (j+1) == life->w ? 0 : j+1; + pos[SW][0] = (i+1) == life->h ? 0 : i+1; pos[SW][1] = (j-1) < 0 ? life->w-1 : j-1; + pos[S ][0] = (i+1) == life->h ? 0 : i+1; pos[S ][1] = j ; + pos[SE][0] = (i+1) == life->h ? 0 : i+1; pos[SE][1] = (j+1) == life->w ? 0 : j+1; + } else { + pos[NW][0] = (i-1) < 0 ? -1 : i-1; pos[NW][1] = (j-1) < 0 ? -1 : j-1; + pos[N ][0] = (i-1) < 0 ? -1 : i-1; pos[N ][1] = j ; + pos[NE][0] = (i-1) < 0 ? -1 : i-1; pos[NE][1] = (j+1) == life->w ? -1 : j+1; + pos[W ][0] = i ; pos[W ][1] = (j-1) < 0 ? -1 : j-1; + pos[E ][0] = i ; pos[E ][1] = (j+1) == life->w ? -1 : j+1; + pos[SW][0] = (i+1) == life->h ? -1 : i+1; pos[SW][1] = (j-1) < 0 ? -1 : j-1; + pos[S ][0] = (i+1) == life->h ? -1 : i+1; pos[S ][1] = j ; + pos[SE][0] = (i+1) == life->h ? -1 : i+1; pos[SE][1] = (j+1) == life->w ? -1 : j+1; + } + + /* compute the number of live neighbor cells */ + n = (pos[NW][0] == -1 || pos[NW][1] == -1 ? 0 : oldbuf[pos[NW][0]*life->w + pos[NW][1]] == ALIVE_CELL) + + (pos[N ][0] == -1 || pos[N ][1] == -1 ? 0 : oldbuf[pos[N ][0]*life->w + pos[N ][1]] == ALIVE_CELL) + + (pos[NE][0] == -1 || pos[NE][1] == -1 ? 0 : oldbuf[pos[NE][0]*life->w + pos[NE][1]] == ALIVE_CELL) + + (pos[W ][0] == -1 || pos[W ][1] == -1 ? 0 : oldbuf[pos[W ][0]*life->w + pos[W ][1]] == ALIVE_CELL) + + (pos[E ][0] == -1 || pos[E ][1] == -1 ? 0 : oldbuf[pos[E ][0]*life->w + pos[E ][1]] == ALIVE_CELL) + + (pos[SW][0] == -1 || pos[SW][1] == -1 ? 0 : oldbuf[pos[SW][0]*life->w + pos[SW][1]] == ALIVE_CELL) + + (pos[S ][0] == -1 || pos[S ][1] == -1 ? 0 : oldbuf[pos[S ][0]*life->w + pos[S ][1]] == ALIVE_CELL) + + (pos[SE][0] == -1 || pos[SE][1] == -1 ? 0 : oldbuf[pos[SE][0]*life->w + pos[SE][1]] == ALIVE_CELL); + cell = oldbuf[i*life->w + j]; + alive = 1<stay_rule : life->born_rule); + if (alive) *newbuf = ALIVE_CELL; // new cell is alive + else if (cell) *newbuf = cell - 1; // new cell is dead and in the process of mold + else *newbuf = 0; // new cell is definitely dead + av_dlog(ctx, "i:%d j:%d live_neighbors:%d cell:%d -> cell:%d\n", i, j, n, cell, *newbuf); + newbuf++; + } + } + + life->buf_idx = !life->buf_idx; +} + +static void fill_picture_monoblack(AVFilterContext *ctx, AVFrame *picref) +{ + LifeContext *life = ctx->priv; + uint8_t *buf = life->buf[life->buf_idx]; + int i, j, k; + + /* fill the output picture with the old grid buffer */ + for (i = 0; i < life->h; i++) { + uint8_t byte = 0; + uint8_t *p = picref->data[0] + i * picref->linesize[0]; + for (k = 0, j = 0; j < life->w; j++) { + byte |= (buf[i*life->w+j] == ALIVE_CELL)<<(7-k++); + if (k==8 || j == life->w-1) { + k = 0; + *p++ = byte; + byte = 0; + } + } + } +} + +// divide by 255 and round to nearest +// apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16 +#define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) + +static void fill_picture_rgb(AVFilterContext *ctx, AVFrame *picref) +{ + LifeContext *life = ctx->priv; + uint8_t *buf = life->buf[life->buf_idx]; + int i, j; + + /* fill the output picture with the old grid buffer */ + for (i = 0; i < life->h; i++) { + uint8_t *p = picref->data[0] + i * picref->linesize[0]; + for (j = 0; j < life->w; j++) { + uint8_t v = buf[i*life->w + j]; + if (life->mold && v != ALIVE_CELL) { + const uint8_t *c1 = life-> mold_color; + const uint8_t *c2 = life->death_color; + int death_age = FFMIN((0xff - v) * life->mold, 0xff); + *p++ = FAST_DIV255((c2[0] << 8) + ((int)c1[0] - (int)c2[0]) * death_age); + *p++ = FAST_DIV255((c2[1] << 8) + ((int)c1[1] - (int)c2[1]) * death_age); + *p++ = FAST_DIV255((c2[2] << 8) + ((int)c1[2] - (int)c2[2]) * death_age); + } else { + const uint8_t *c = v == ALIVE_CELL ? life->life_color : life->death_color; + AV_WB24(p, c[0]<<16 | c[1]<<8 | c[2]); + p += 3; + } + } + } +} + +static int request_frame(AVFilterLink *outlink) +{ + LifeContext *life = outlink->src->priv; + AVFrame *picref = ff_get_video_buffer(outlink, life->w, life->h); + if (!picref) + return AVERROR(ENOMEM); + picref->sample_aspect_ratio = (AVRational) {1, 1}; + picref->pts = life->pts++; + + life->draw(outlink->src, picref); + evolve(outlink->src); +#ifdef DEBUG + show_life_grid(outlink->src); +#endif + return ff_filter_frame(outlink, picref); +} + +static int query_formats(AVFilterContext *ctx) +{ + LifeContext *life = ctx->priv; + enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_NONE, AV_PIX_FMT_NONE }; + if (life->mold || memcmp(life-> life_color, "\xff\xff\xff", 3) + || memcmp(life->death_color, "\x00\x00\x00", 3)) { + pix_fmts[0] = AV_PIX_FMT_RGB24; + life->draw = fill_picture_rgb; + } else { + pix_fmts[0] = AV_PIX_FMT_MONOBLACK; + life->draw = fill_picture_monoblack; + } + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static const AVFilterPad life_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL} +}; + +AVFilter avfilter_vsrc_life = { + .name = "life", + .description = NULL_IF_CONFIG_SMALL("Create life."), + .priv_size = sizeof(LifeContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = NULL, + .outputs = life_outputs, + .priv_class = &life_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_life.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_life.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavfilter/vsrc_life.o libavfilter/vsrc_life.o: libavfilter/vsrc_life.c \ + libavutil/file.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/lfg.h libavutil/opt.h libavutil/samplefmt.h \ + libavutil/parseutils.h libavutil/random_seed.h libavutil/avstring.h \ + libavfilter/avfilter.h libavutil/avutil.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/log.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavfilter/version.h \ + libavutil/avutil.h libavfilter/internal.h libavfilter/avfiltergraph.h \ + libavfilter/formats.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_life.o Binary file ffmpeg/libavfilter/vsrc_life.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_mandelbrot.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_mandelbrot.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2011 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The vsrc_color filter from Stefano Sabatini was used as template to create + * this + */ + +/** + * @file + * Mandelbrot fraktal renderer + */ + +#include "avfilter.h" +#include "formats.h" +#include "video.h" +#include "internal.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include +#include + +#define SQR(a) ((a)*(a)) + +enum Outer{ + ITERATION_COUNT, + NORMALIZED_ITERATION_COUNT, + WHITE, + OUTZ, +}; + +enum Inner{ + BLACK, + PERIOD, + CONVTIME, + MINCOL, +}; + +typedef struct Point { + double p[2]; + uint32_t val; +} Point; + +typedef struct { + const AVClass *class; + int w, h; + AVRational frame_rate; + uint64_t pts; + int maxiter; + double start_x; + double start_y; + double start_scale; + double end_scale; + double end_pts; + double bailout; + enum Outer outer; + enum Inner inner; + int cache_allocated; + int cache_used; + Point *point_cache; + Point *next_cache; + double (*zyklus)[2]; + uint32_t dither; + + double morphxf; + double morphyf; + double morphamp; +} MBContext; + +#define OFFSET(x) offsetof(MBContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption mandelbrot_options[] = { + {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, CHAR_MIN, CHAR_MAX, FLAGS }, + {"s", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, CHAR_MIN, CHAR_MAX, FLAGS }, + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, CHAR_MIN, CHAR_MAX, FLAGS }, + {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, CHAR_MIN, CHAR_MAX, FLAGS }, + {"maxiter", "set max iterations number", OFFSET(maxiter), AV_OPT_TYPE_INT, {.i64=7189}, 1, INT_MAX, FLAGS }, + {"start_x", "set the initial x position", OFFSET(start_x), AV_OPT_TYPE_DOUBLE, {.dbl=-0.743643887037158704752191506114774}, -100, 100, FLAGS }, + {"start_y", "set the initial y position", OFFSET(start_y), AV_OPT_TYPE_DOUBLE, {.dbl=-0.131825904205311970493132056385139}, -100, 100, FLAGS }, + {"start_scale", "set the initial scale value", OFFSET(start_scale), AV_OPT_TYPE_DOUBLE, {.dbl=3.0}, 0, FLT_MAX, FLAGS }, + {"end_scale", "set the terminal scale value", OFFSET(end_scale), AV_OPT_TYPE_DOUBLE, {.dbl=0.3}, 0, FLT_MAX, FLAGS }, + {"end_pts", "set the terminal pts value", OFFSET(end_pts), AV_OPT_TYPE_DOUBLE, {.dbl=400}, 0, INT64_MAX, FLAGS }, + {"bailout", "set the bailout value", OFFSET(bailout), AV_OPT_TYPE_DOUBLE, {.dbl=10}, 0, FLT_MAX, FLAGS }, + {"morphxf", "set morph x frequency", OFFSET(morphxf), AV_OPT_TYPE_DOUBLE, {.dbl=0.01}, -FLT_MAX, FLT_MAX, FLAGS }, + {"morphyf", "set morph y frequency", OFFSET(morphyf), AV_OPT_TYPE_DOUBLE, {.dbl=0.0123}, -FLT_MAX, FLT_MAX, FLAGS }, + {"morphamp", "set morph amplitude", OFFSET(morphamp), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, + + {"outer", "set outer coloring mode", OFFSET(outer), AV_OPT_TYPE_INT, {.i64=NORMALIZED_ITERATION_COUNT}, 0, INT_MAX, FLAGS, "outer" }, + {"iteration_count", "set iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" }, + {"normalized_iteration_count", "set normalized iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=NORMALIZED_ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" }, + {"white", "set white mode", 0, AV_OPT_TYPE_CONST, {.i64=WHITE}, INT_MIN, INT_MAX, FLAGS, "outer" }, + {"outz", "set outz mode", 0, AV_OPT_TYPE_CONST, {.i64=OUTZ}, INT_MIN, INT_MAX, FLAGS, "outer" }, + + {"inner", "set inner coloring mode", OFFSET(inner), AV_OPT_TYPE_INT, {.i64=MINCOL}, 0, INT_MAX, FLAGS, "inner" }, + {"black", "set black mode", 0, AV_OPT_TYPE_CONST, {.i64=BLACK}, INT_MIN, INT_MAX, FLAGS, "inner"}, + {"period", "set period mode", 0, AV_OPT_TYPE_CONST, {.i64=PERIOD}, INT_MIN, INT_MAX, FLAGS, "inner"}, + {"convergence", "show time until convergence", 0, AV_OPT_TYPE_CONST, {.i64=CONVTIME}, INT_MIN, INT_MAX, FLAGS, "inner"}, + {"mincol", "color based on point closest to the origin of the iterations", 0, AV_OPT_TYPE_CONST, {.i64=MINCOL}, INT_MIN, INT_MAX, FLAGS, "inner"}, + + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(mandelbrot); + +static av_cold int init(AVFilterContext *ctx) +{ + MBContext *mb = ctx->priv; + + mb->bailout *= mb->bailout; + + mb->start_scale /=mb->h; + mb->end_scale /=mb->h; + + mb->cache_allocated = mb->w * mb->h * 3; + mb->cache_used = 0; + mb->point_cache= av_malloc(sizeof(*mb->point_cache)*mb->cache_allocated); + mb-> next_cache= av_malloc(sizeof(*mb-> next_cache)*mb->cache_allocated); + mb-> zyklus = av_malloc(sizeof(*mb->zyklus) * (mb->maxiter+16)); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + MBContext *mb = ctx->priv; + + av_freep(&mb->point_cache); + av_freep(&mb-> next_cache); + av_freep(&mb->zyklus); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_BGR32, + AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static int config_props(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->src; + MBContext *mb = ctx->priv; + + if (av_image_check_size(mb->w, mb->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + inlink->w = mb->w; + inlink->h = mb->h; + inlink->time_base = av_inv_q(mb->frame_rate); + + return 0; +} + +static void fill_from_cache(AVFilterContext *ctx, uint32_t *color, int *in_cidx, int *out_cidx, double py, double scale){ + MBContext *mb = ctx->priv; + if(mb->morphamp) + return; + for(; *in_cidx < mb->cache_used; (*in_cidx)++){ + Point *p= &mb->point_cache[*in_cidx]; + int x; + if(p->p[1] > py) + break; + x= round((p->p[0] - mb->start_x) / scale + mb->w/2); + if(x<0 || x >= mb->w) + continue; + if(color) color[x] = p->val; + if(out_cidx && *out_cidx < mb->cache_allocated) + mb->next_cache[(*out_cidx)++]= *p; + } +} + +static int interpol(MBContext *mb, uint32_t *color, int x, int y, int linesize) +{ + uint32_t a,b,c,d, i; + uint32_t ipol=0xFF000000; + int dist; + + if(!x || !y || x+1==mb->w || y+1==mb->h) + return 0; + + dist= FFMAX(FFABS(x-(mb->w>>1))*mb->h, FFABS(y-(mb->h>>1))*mb->w); + + if(dist<(mb->w*mb->h>>3)) + return 0; + + a=color[(x+1) + (y+0)*linesize]; + b=color[(x-1) + (y+1)*linesize]; + c=color[(x+0) + (y+1)*linesize]; + d=color[(x+1) + (y+1)*linesize]; + + if(a&&c){ + b= color[(x-1) + (y+0)*linesize]; + d= color[(x+0) + (y-1)*linesize]; + }else if(b&&d){ + a= color[(x+1) + (y-1)*linesize]; + c= color[(x-1) + (y-1)*linesize]; + }else if(c){ + d= color[(x+0) + (y-1)*linesize]; + a= color[(x-1) + (y+0)*linesize]; + b= color[(x+1) + (y-1)*linesize]; + }else if(d){ + c= color[(x-1) + (y-1)*linesize]; + a= color[(x-1) + (y+0)*linesize]; + b= color[(x+1) + (y-1)*linesize]; + }else + return 0; + + for(i=0; i<3; i++){ + int s= 8*i; + uint8_t ac= a>>s; + uint8_t bc= b>>s; + uint8_t cc= c>>s; + uint8_t dc= d>>s; + int ipolab= (ac + bc); + int ipolcd= (cc + dc); + if(FFABS(ipolab - ipolcd) > 5) + return 0; + if(FFABS(ac-bc)+FFABS(cc-dc) > 20) + return 0; + ipol |= ((ipolab + ipolcd + 2)/4)<priv; + int x,y,i, in_cidx=0, next_cidx=0, tmp_cidx; + double scale= mb->start_scale*pow(mb->end_scale/mb->start_scale, pts/mb->end_pts); + int use_zyklus=0; + fill_from_cache(ctx, NULL, &in_cidx, NULL, mb->start_y+scale*(-mb->h/2-0.5), scale); + tmp_cidx= in_cidx; + memset(color, 0, sizeof(*color)*mb->w); + for(y=0; yh; y++){ + int y1= y+1; + const double ci=mb->start_y+scale*(y-mb->h/2); + fill_from_cache(ctx, NULL, &in_cidx, &next_cidx, ci, scale); + if(y1h){ + memset(color+linesize*y1, 0, sizeof(*color)*mb->w); + fill_from_cache(ctx, color+linesize*y1, &tmp_cidx, NULL, ci + 3*scale/2, scale); + } + + for(x=0; xw; x++){ + float av_uninit(epsilon); + const double cr=mb->start_x+scale*(x-mb->w/2); + double zr=cr; + double zi=ci; + uint32_t c=0; + double dv= mb->dither / (double)(1LL<<32); + mb->dither= mb->dither*1664525+1013904223; + + if(color[x + y*linesize] & 0xFF000000) + continue; + if(!mb->morphamp){ + if(interpol(mb, color, x, y, linesize)){ + if(next_cidx < mb->cache_allocated){ + mb->next_cache[next_cidx ].p[0]= cr; + mb->next_cache[next_cidx ].p[1]= ci; + mb->next_cache[next_cidx++].val = color[x + y*linesize]; + } + continue; + } + }else{ + zr += cos(pts * mb->morphxf) * mb->morphamp; + zi += sin(pts * mb->morphyf) * mb->morphamp; + } + + use_zyklus= (x==0 || mb->inner!=BLACK ||color[x-1 + y*linesize] == 0xFF000000); + if(use_zyklus) + epsilon= scale*1*sqrt(SQR(x-mb->w/2) + SQR(y-mb->h/2))/mb->w; + +#define Z_Z2_C(outr,outi,inr,ini)\ + outr= inr*inr - ini*ini + cr;\ + outi= 2*inr*ini + ci; + +#define Z_Z2_C_ZYKLUS(outr,outi,inr,ini, Z)\ + Z_Z2_C(outr,outi,inr,ini)\ + if(use_zyklus){\ + if(Z && fabs(mb->zyklus[i>>1][0]-outr)+fabs(mb->zyklus[i>>1][1]-outi) <= epsilon)\ + break;\ + }\ + mb->zyklus[i][0]= outr;\ + mb->zyklus[i][1]= outi;\ + + + + for(i=0; imaxiter-8; i++){ + double t; + Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0) + i++; + Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1) + i++; + Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0) + i++; + Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1) + i++; + Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0) + i++; + Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1) + i++; + Z_Z2_C_ZYKLUS(t, zi, zr, zi, 0) + i++; + Z_Z2_C_ZYKLUS(zr, zi, t, zi, 1) + if(zr*zr + zi*zi > mb->bailout){ + i-= FFMIN(7, i); + for(; imaxiter; i++){ + zr= mb->zyklus[i][0]; + zi= mb->zyklus[i][1]; + if(zr*zr + zi*zi > mb->bailout){ + switch(mb->outer){ + case ITERATION_COUNT: + zr = i; + c = lrintf((sin(zr)+1)*127) + lrintf((sin(zr/1.234)+1)*127)*256*256 + lrintf((sin(zr/100)+1)*127)*256; + break; + case NORMALIZED_ITERATION_COUNT: + zr = i + log2(log(mb->bailout) / log(zr*zr + zi*zi)); + c = lrintf((sin(zr)+1)*127) + lrintf((sin(zr/1.234)+1)*127)*256*256 + lrintf((sin(zr/100)+1)*127)*256; + break; + case WHITE: + c = 0xFFFFFF; + break; + case OUTZ: + zr /= mb->bailout; + zi /= mb->bailout; + c = (((int)(zr*128+128))&0xFF)*256 + (((int)(zi*128+128))&0xFF); + } + break; + } + } + break; + } + } + if(!c){ + if(mb->inner==PERIOD){ + int j; + for(j=i-1; j; j--) + if(SQR(mb->zyklus[j][0]-zr) + SQR(mb->zyklus[j][1]-zi) < epsilon*epsilon*10) + break; + if(j){ + c= i-j; + c= ((c<<5)&0xE0) + ((c<<10)&0xE000) + ((c<<15)&0xE00000); + } + }else if(mb->inner==CONVTIME){ + c= floor(i*255.0/mb->maxiter+dv)*0x010101; + } else if(mb->inner==MINCOL){ + int j; + double closest=9999; + int closest_index=0; + for(j=i-1; j>=0; j--) + if(SQR(mb->zyklus[j][0]) + SQR(mb->zyklus[j][1]) < closest){ + closest= SQR(mb->zyklus[j][0]) + SQR(mb->zyklus[j][1]); + closest_index= j; + } + closest = sqrt(closest); + c= lrintf((mb->zyklus[closest_index][0]/closest+1)*127+dv) + lrintf((mb->zyklus[closest_index][1]/closest+1)*127+dv)*256; + } + } + c |= 0xFF000000; + color[x + y*linesize]= c; + if(next_cidx < mb->cache_allocated){ + mb->next_cache[next_cidx ].p[0]= cr; + mb->next_cache[next_cidx ].p[1]= ci; + mb->next_cache[next_cidx++].val = c; + } + } + fill_from_cache(ctx, NULL, &in_cidx, &next_cidx, ci + scale/2, scale); + } + FFSWAP(void*, mb->next_cache, mb->point_cache); + mb->cache_used = next_cidx; + if(mb->cache_used == mb->cache_allocated) + av_log(ctx, AV_LOG_INFO, "Mandelbrot cache is too small!\n"); +} + +static int request_frame(AVFilterLink *link) +{ + MBContext *mb = link->src->priv; + AVFrame *picref = ff_get_video_buffer(link, mb->w, mb->h); + if (!picref) + return AVERROR(ENOMEM); + + picref->sample_aspect_ratio = (AVRational) {1, 1}; + picref->pts = mb->pts++; + + draw_mandelbrot(link->src, (uint32_t*)picref->data[0], picref->linesize[0]/4, picref->pts); + return ff_filter_frame(link, picref); +} + +static const AVFilterPad mandelbrot_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL }, +}; + +AVFilter avfilter_vsrc_mandelbrot = { + .name = "mandelbrot", + .description = NULL_IF_CONFIG_SMALL("Render a Mandelbrot fractal."), + + .priv_size = sizeof(MBContext), + .init = init, + .uninit = uninit, + + .query_formats = query_formats, + .inputs = NULL, + .outputs = mandelbrot_outputs, + .priv_class = &mandelbrot_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_mandelbrot.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_mandelbrot.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavfilter/vsrc_mandelbrot.o libavfilter/vsrc_mandelbrot.o: \ + libavfilter/vsrc_mandelbrot.c libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/formats.h \ + libavfilter/video.h libavfilter/internal.h libavfilter/avfiltergraph.h \ + libavutil/imgutils.h libavutil/pixdesc.h libavutil/opt.h \ + libavutil/parseutils.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_mandelbrot.o Binary file ffmpeg/libavfilter/vsrc_mandelbrot.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_mptestsrc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_mptestsrc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2002 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file + * MP test source, ported from MPlayer libmpcodecs/vf_test.c + */ + +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "internal.h" +#include "formats.h" +#include "video.h" + +#define WIDTH 512 +#define HEIGHT 512 + +enum test_type { + TEST_DC_LUMA, + TEST_DC_CHROMA, + TEST_FREQ_LUMA, + TEST_FREQ_CHROMA, + TEST_AMP_LUMA, + TEST_AMP_CHROMA, + TEST_CBP, + TEST_MV, + TEST_RING1, + TEST_RING2, + TEST_ALL, + TEST_NB +}; + +typedef struct MPTestContext { + const AVClass *class; + unsigned int frame_nb; + AVRational frame_rate; + int64_t pts, max_pts, duration; + int hsub, vsub; + enum test_type test; +} MPTestContext; + +#define OFFSET(x) offsetof(MPTestContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +static const AVOption mptestsrc_options[]= { + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, + { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, + { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, + + { "test", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, + { "t", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, + { "dc_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "dc_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "freq_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "freq_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "amp_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "amp_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "cbp", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_CBP}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "mv", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_MV}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "ring1", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING1}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "ring2", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING2}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_ALL}, INT_MIN, INT_MAX, FLAGS, "test" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(mptestsrc); + +static double c[64]; + +static void init_idct(void) +{ + int i, j; + + for (i = 0; i < 8; i++) { + double s = i == 0 ? sqrt(0.125) : 0.5; + + for (j = 0; j < 8; j++) + c[i*8+j] = s*cos((M_PI/8.0)*i*(j+0.5)); + } +} + +static void idct(uint8_t *dst, int dst_linesize, int src[64]) +{ + int i, j, k; + double tmp[64]; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + double sum = 0.0; + + for (k = 0; k < 8; k++) + sum += c[k*8+j] * src[8*i+k]; + + tmp[8*i+j] = sum; + } + } + + for (j = 0; j < 8; j++) { + for (i = 0; i < 8; i++) { + double sum = 0.0; + + for (k = 0; k < 8; k++) + sum += c[k*8+i]*tmp[8*k+j]; + + dst[dst_linesize*i + j] = av_clip((int)floor(sum+0.5), 0, 255); + } + } +} + +static void draw_dc(uint8_t *dst, int dst_linesize, int color, int w, int h) +{ + int x, y; + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + dst[x + y*dst_linesize] = color; +} + +static void draw_basis(uint8_t *dst, int dst_linesize, int amp, int freq, int dc) +{ + int src[64]; + + memset(src, 0, 64*sizeof(int)); + src[0] = dc; + if (amp) + src[freq] = amp; + idct(dst, dst_linesize, src); +} + +static void draw_cbp(uint8_t *dst[3], int dst_linesize[3], int cbp, int amp, int dc) +{ + if (cbp&1) draw_basis(dst[0] , dst_linesize[0], amp, 1, dc); + if (cbp&2) draw_basis(dst[0]+8 , dst_linesize[0], amp, 1, dc); + if (cbp&4) draw_basis(dst[0]+ 8*dst_linesize[0], dst_linesize[0], amp, 1, dc); + if (cbp&8) draw_basis(dst[0]+8+8*dst_linesize[0], dst_linesize[0], amp, 1, dc); + if (cbp&16) draw_basis(dst[1] , dst_linesize[1], amp, 1, dc); + if (cbp&32) draw_basis(dst[2] , dst_linesize[2], amp, 1, dc); +} + +static void dc_test(uint8_t *dst, int dst_linesize, int w, int h, int off) +{ + const int step = FFMAX(256/(w*h/256), 1); + int x, y, color = off; + + for (y = 0; y < h; y += 16) { + for (x = 0; x < w; x += 16) { + draw_dc(dst + x + y*dst_linesize, dst_linesize, color, 8, 8); + color += step; + } + } +} + +static void freq_test(uint8_t *dst, int dst_linesize, int off) +{ + int x, y, freq = 0; + + for (y = 0; y < 8*16; y += 16) { + for (x = 0; x < 8*16; x += 16) { + draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*(96+off), freq, 128*8); + freq++; + } + } +} + +static void amp_test(uint8_t *dst, int dst_linesize, int off) +{ + int x, y, amp = off; + + for (y = 0; y < 16*16; y += 16) { + for (x = 0; x < 16*16; x += 16) { + draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*amp, 1, 128*8); + amp++; + } + } +} + +static void cbp_test(uint8_t *dst[3], int dst_linesize[3], int off) +{ + int x, y, cbp = 0; + + for (y = 0; y < 16*8; y += 16) { + for (x = 0; x < 16*8; x += 16) { + uint8_t *dst1[3]; + dst1[0] = dst[0] + x*2 + y*2*dst_linesize[0]; + dst1[1] = dst[1] + x + y* dst_linesize[1]; + dst1[2] = dst[2] + x + y* dst_linesize[2]; + + draw_cbp(dst1, dst_linesize, cbp, (64+off)*4, 128*8); + cbp++; + } + } +} + +static void mv_test(uint8_t *dst, int dst_linesize, int off) +{ + int x, y; + + for (y = 0; y < 16*16; y++) { + if (y&16) + continue; + for (x = 0; x < 16*16; x++) + dst[x + y*dst_linesize] = x + off*8/(y/32+1); + } +} + +static void ring1_test(uint8_t *dst, int dst_linesize, int off) +{ + int x, y, color = 0; + + for (y = off; y < 16*16; y += 16) { + for (x = off; x < 16*16; x += 16) { + draw_dc(dst + x + y*dst_linesize, dst_linesize, ((x+y)&16) ? color : -color, 16, 16); + color++; + } + } +} + +static void ring2_test(uint8_t *dst, int dst_linesize, int off) +{ + int x, y; + + for (y = 0; y < 16*16; y++) { + for (x = 0; x < 16*16; x++) { + double d = sqrt((x-8*16)*(x-8*16) + (y-8*16)*(y-8*16)); + double r = d/20 - (int)(d/20); + if (r < off/30.0) { + dst[x + y*dst_linesize] = 255; + dst[x + y*dst_linesize+256] = 0; + } else { + dst[x + y*dst_linesize] = x; + dst[x + y*dst_linesize+256] = x; + } + } + } +} + +static av_cold int init(AVFilterContext *ctx) +{ + MPTestContext *test = ctx->priv; + + test->max_pts = test->duration >= 0 ? + av_rescale_q(test->duration, AV_TIME_BASE_Q, av_inv_q(test->frame_rate)) : -1; + test->frame_nb = 0; + test->pts = 0; + + av_log(ctx, AV_LOG_VERBOSE, "rate:%d/%d duration:%f\n", + test->frame_rate.num, test->frame_rate.den, + test->duration < 0 ? -1 : test->max_pts * av_q2d(av_inv_q(test->frame_rate))); + init_idct(); + + return 0; +} + +static int config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + MPTestContext *test = ctx->priv; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format); + + test->hsub = pix_desc->log2_chroma_w; + test->vsub = pix_desc->log2_chroma_h; + + outlink->w = WIDTH; + outlink->h = HEIGHT; + outlink->time_base = av_inv_q(test->frame_rate); + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + MPTestContext *test = outlink->src->priv; + AVFrame *picref; + int w = WIDTH, h = HEIGHT, cw = w>>test->hsub, ch = h>>test->vsub; + unsigned int frame = test->frame_nb; + enum test_type tt = test->test; + int i; + + if (test->max_pts >= 0 && test->pts > test->max_pts) + return AVERROR_EOF; + picref = ff_get_video_buffer(outlink, w, h); + if (!picref) + return AVERROR(ENOMEM); + picref->pts = test->pts++; + + // clean image + for (i = 0; i < h; i++) + memset(picref->data[0] + i*picref->linesize[0], 0, w); + for (i = 0; i < ch; i++) { + memset(picref->data[1] + i*picref->linesize[1], 128, cw); + memset(picref->data[2] + i*picref->linesize[2], 128, cw); + } + + if (tt == TEST_ALL && frame%30) /* draw a black frame at the beginning of each test */ + tt = (frame/30)%(TEST_NB-1); + + switch (tt) { + case TEST_DC_LUMA: dc_test(picref->data[0], picref->linesize[0], 256, 256, frame%30); break; + case TEST_DC_CHROMA: dc_test(picref->data[1], picref->linesize[1], 256, 256, frame%30); break; + case TEST_FREQ_LUMA: freq_test(picref->data[0], picref->linesize[0], frame%30); break; + case TEST_FREQ_CHROMA: freq_test(picref->data[1], picref->linesize[1], frame%30); break; + case TEST_AMP_LUMA: amp_test(picref->data[0], picref->linesize[0], frame%30); break; + case TEST_AMP_CHROMA: amp_test(picref->data[1], picref->linesize[1], frame%30); break; + case TEST_CBP: cbp_test(picref->data , picref->linesize , frame%30); break; + case TEST_MV: mv_test(picref->data[0], picref->linesize[0], frame%30); break; + case TEST_RING1: ring1_test(picref->data[0], picref->linesize[0], frame%30); break; + case TEST_RING2: ring2_test(picref->data[0], picref->linesize[0], frame%30); break; + } + + test->frame_nb++; + return ff_filter_frame(outlink, picref); +} + +static const AVFilterPad mptestsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL } +}; + +AVFilter avfilter_vsrc_mptestsrc = { + .name = "mptestsrc", + .description = NULL_IF_CONFIG_SMALL("Generate various test pattern."), + .priv_size = sizeof(MPTestContext), + .init = init, + + .query_formats = query_formats, + + .inputs = NULL, + .outputs = mptestsrc_outputs, + .priv_class = &mptestsrc_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_testsrc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_testsrc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2007 Nicolas George + * Copyright (c) 2011 Stefano Sabatini + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Misc test sources. + * + * testsrc is based on the test pattern generator demuxer by Nicolas George: + * http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2007-October/037845.html + * + * rgbtestsrc is ported from MPlayer libmpcodecs/vf_rgbtest.c by + * Michael Niedermayer. + * + * smptebars and smptehdbars are by Paul B Mahol. + */ + +#include + +#include "libavutil/avassert.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" +#include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/parseutils.h" +#include "avfilter.h" +#include "drawutils.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct { + const AVClass *class; + int w, h; + unsigned int nb_frame; + AVRational time_base, frame_rate; + int64_t pts; + int64_t duration; ///< duration expressed in microseconds + AVRational sar; ///< sample aspect ratio + int nb_decimals; + int draw_once; ///< draw only the first frame, always put out the same picture + AVFrame *picref; ///< cached reference containing the painted picture + + void (* fill_picture_fn)(AVFilterContext *ctx, AVFrame *frame); + + /* only used by color */ + char *color_str; + FFDrawContext draw; + FFDrawColor color; + uint8_t color_rgba[4]; + + /* only used by rgbtest */ + uint8_t rgba_map[4]; +} TestSourceContext; + +#define OFFSET(x) offsetof(TestSourceContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +#define COMMON_OPTIONS \ + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS },\ + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS },\ + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },\ + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },\ + { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },\ + { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },\ + { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, {.dbl= 1}, 0, INT_MAX, FLAGS }, + + +static const AVOption color_options[] = { + /* only used by color */ + { "color", "set color", OFFSET(color_str), AV_OPT_TYPE_STRING, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "c", "set color", OFFSET(color_str), AV_OPT_TYPE_STRING, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + + COMMON_OPTIONS + { NULL }, +}; + +static const AVOption options[] = { + COMMON_OPTIONS + /* only used by testsrc */ + { "decimals", "set number of decimals to show", OFFSET(nb_decimals), AV_OPT_TYPE_INT, {.i64=0}, 0, 17, FLAGS }, + { "n", "set number of decimals to show", OFFSET(nb_decimals), AV_OPT_TYPE_INT, {.i64=0}, 0, 17, FLAGS }, + + { NULL }, +}; + +static av_cold int init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + int ret = 0; + + if (test->nb_decimals && strcmp(ctx->filter->name, "testsrc")) { + av_log(ctx, AV_LOG_WARNING, + "Option 'decimals' is ignored with source '%s'\n", + ctx->filter->name); + } + + if (test->color_str) { + if (!strcmp(ctx->filter->name, "color")) { + ret = av_parse_color(test->color_rgba, test->color_str, -1, ctx); + if (ret < 0) + return ret; + } else { + av_log(ctx, AV_LOG_WARNING, + "Option 'color' is ignored with source '%s'\n", + ctx->filter->name); + } + } + + test->time_base = av_inv_q(test->frame_rate); + test->nb_frame = 0; + test->pts = 0; + + av_log(ctx, AV_LOG_VERBOSE, "size:%dx%d rate:%d/%d duration:%f sar:%d/%d\n", + test->w, test->h, test->frame_rate.num, test->frame_rate.den, + test->duration < 0 ? -1 : (double)test->duration/1000000, + test->sar.num, test->sar.den); + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + av_frame_free(&test->picref); +} + +static int config_props(AVFilterLink *outlink) +{ + TestSourceContext *test = outlink->src->priv; + + outlink->w = test->w; + outlink->h = test->h; + outlink->sample_aspect_ratio = test->sar; + outlink->frame_rate = test->frame_rate; + outlink->time_base = test->time_base; + + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + TestSourceContext *test = outlink->src->priv; + AVFrame *frame; + + if (test->duration >= 0 && + av_rescale_q(test->pts, test->time_base, AV_TIME_BASE_Q) >= test->duration) + return AVERROR_EOF; + + if (test->draw_once) { + if (!test->picref) { + test->picref = + ff_get_video_buffer(outlink, test->w, test->h); + if (!test->picref) + return AVERROR(ENOMEM); + test->fill_picture_fn(outlink->src, test->picref); + } + frame = av_frame_clone(test->picref); + } else + frame = ff_get_video_buffer(outlink, test->w, test->h); + + if (!frame) + return AVERROR(ENOMEM); + frame->pts = test->pts; + frame->key_frame = 1; + frame->interlaced_frame = 0; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->sample_aspect_ratio = test->sar; + if (!test->draw_once) + test->fill_picture_fn(outlink->src, frame); + + test->pts++; + test->nb_frame++; + + return ff_filter_frame(outlink, frame); +} + +#if CONFIG_COLOR_FILTER + +AVFILTER_DEFINE_CLASS(color); + +static void color_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + ff_fill_rectangle(&test->draw, &test->color, + picref->data, picref->linesize, + 0, 0, test->w, test->h); +} + +static av_cold int color_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + test->fill_picture_fn = color_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +static int color_query_formats(AVFilterContext *ctx) +{ + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + return 0; +} + +static int color_config_props(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->src; + TestSourceContext *test = ctx->priv; + int ret; + + ff_draw_init(&test->draw, inlink->format, 0); + ff_draw_color(&test->draw, &test->color, test->color_rgba); + + test->w = ff_draw_round_to_sub(&test->draw, 0, -1, test->w); + test->h = ff_draw_round_to_sub(&test->draw, 1, -1, test->h); + if (av_image_check_size(test->w, test->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + if ((ret = config_props(inlink)) < 0) + return ret; + + av_log(ctx, AV_LOG_VERBOSE, "color:0x%02x%02x%02x%02x\n", + test->color_rgba[0], test->color_rgba[1], test->color_rgba[2], test->color_rgba[3]); + return 0; +} + +static const AVFilterPad color_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = color_config_props, + }, + { NULL } +}; + +AVFilter avfilter_vsrc_color = { + .name = "color", + .description = NULL_IF_CONFIG_SMALL("Provide an uniformly colored input."), + + .priv_class = &color_class, + .priv_size = sizeof(TestSourceContext), + .init = color_init, + .uninit = uninit, + + .query_formats = color_query_formats, + .inputs = NULL, + .outputs = color_outputs, +}; + +#endif /* CONFIG_COLOR_FILTER */ + +#if CONFIG_NULLSRC_FILTER + +#define nullsrc_options options +AVFILTER_DEFINE_CLASS(nullsrc); + +static void nullsrc_fill_picture(AVFilterContext *ctx, AVFrame *picref) { } + +static av_cold int nullsrc_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = nullsrc_fill_picture; + return init(ctx); +} + +static const AVFilterPad nullsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL }, +}; + +AVFilter avfilter_vsrc_nullsrc = { + .name = "nullsrc", + .description = NULL_IF_CONFIG_SMALL("Null video source, return unprocessed video frames."), + .init = nullsrc_init, + .uninit = uninit, + .priv_size = sizeof(TestSourceContext), + .priv_class = &nullsrc_class, + .inputs = NULL, + .outputs = nullsrc_outputs, +}; + +#endif /* CONFIG_NULLSRC_FILTER */ + +#if CONFIG_TESTSRC_FILTER + +#define testsrc_options options +AVFILTER_DEFINE_CLASS(testsrc); + +/** + * Fill a rectangle with value val. + * + * @param val the RGB value to set + * @param dst pointer to the destination buffer to fill + * @param dst_linesize linesize of destination + * @param segment_width width of the segment + * @param x horizontal coordinate where to draw the rectangle in the destination buffer + * @param y horizontal coordinate where to draw the rectangle in the destination buffer + * @param w width of the rectangle to draw, expressed as a number of segment_width units + * @param h height of the rectangle to draw, expressed as a number of segment_width units + */ +static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, int segment_width, + int x, int y, int w, int h) +{ + int i; + int step = 3; + + dst += segment_width * (step * x + y * dst_linesize); + w *= segment_width * step; + h *= segment_width; + for (i = 0; i < h; i++) { + memset(dst, val, w); + dst += dst_linesize; + } +} + +static void draw_digit(int digit, uint8_t *dst, int dst_linesize, + int segment_width) +{ +#define TOP_HBAR 1 +#define MID_HBAR 2 +#define BOT_HBAR 4 +#define LEFT_TOP_VBAR 8 +#define LEFT_BOT_VBAR 16 +#define RIGHT_TOP_VBAR 32 +#define RIGHT_BOT_VBAR 64 + struct { + int x, y, w, h; + } segments[] = { + { 1, 0, 5, 1 }, /* TOP_HBAR */ + { 1, 6, 5, 1 }, /* MID_HBAR */ + { 1, 12, 5, 1 }, /* BOT_HBAR */ + { 0, 1, 1, 5 }, /* LEFT_TOP_VBAR */ + { 0, 7, 1, 5 }, /* LEFT_BOT_VBAR */ + { 6, 1, 1, 5 }, /* RIGHT_TOP_VBAR */ + { 6, 7, 1, 5 } /* RIGHT_BOT_VBAR */ + }; + static const unsigned char masks[10] = { + /* 0 */ TOP_HBAR |BOT_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 1 */ RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 2 */ TOP_HBAR|MID_HBAR|BOT_HBAR|LEFT_BOT_VBAR |RIGHT_TOP_VBAR, + /* 3 */ TOP_HBAR|MID_HBAR|BOT_HBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 4 */ MID_HBAR |LEFT_TOP_VBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 5 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR |RIGHT_BOT_VBAR, + /* 6 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR |RIGHT_BOT_VBAR, + /* 7 */ TOP_HBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 8 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + /* 9 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR, + }; + unsigned mask = masks[digit]; + int i; + + draw_rectangle(0, dst, dst_linesize, segment_width, 0, 0, 8, 13); + for (i = 0; i < FF_ARRAY_ELEMS(segments); i++) + if (mask & (1<priv; + uint8_t *p, *p0; + int x, y; + int color, color_rest; + int icolor; + int radius; + int quad0, quad; + int dquad_x, dquad_y; + int grad, dgrad, rgrad, drgrad; + int seg_size; + int second; + int i; + uint8_t *data = frame->data[0]; + int width = frame->width; + int height = frame->height; + + /* draw colored bars and circle */ + radius = (width + height) / 4; + quad0 = width * width / 4 + height * height / 4 - radius * radius; + dquad_y = 1 - height; + p0 = data; + for (y = 0; y < height; y++) { + p = p0; + color = 0; + color_rest = 0; + quad = quad0; + dquad_x = 1 - width; + for (x = 0; x < width; x++) { + icolor = color; + if (quad < 0) + icolor ^= 7; + quad += dquad_x; + dquad_x += 2; + *(p++) = icolor & 1 ? 255 : 0; + *(p++) = icolor & 2 ? 255 : 0; + *(p++) = icolor & 4 ? 255 : 0; + color_rest += 8; + if (color_rest >= width) { + color_rest -= width; + color++; + } + } + quad0 += dquad_y; + dquad_y += 2; + p0 += frame->linesize[0]; + } + + /* draw sliding color line */ + p0 = p = data + frame->linesize[0] * height * 3/4; + grad = (256 * test->nb_frame * test->time_base.num / test->time_base.den) % + GRADIENT_SIZE; + rgrad = 0; + dgrad = GRADIENT_SIZE / width; + drgrad = GRADIENT_SIZE % width; + for (x = 0; x < width; x++) { + *(p++) = + grad < 256 || grad >= 5 * 256 ? 255 : + grad >= 2 * 256 && grad < 4 * 256 ? 0 : + grad < 2 * 256 ? 2 * 256 - 1 - grad : grad - 4 * 256; + *(p++) = + grad >= 4 * 256 ? 0 : + grad >= 1 * 256 && grad < 3 * 256 ? 255 : + grad < 1 * 256 ? grad : 4 * 256 - 1 - grad; + *(p++) = + grad < 2 * 256 ? 0 : + grad >= 3 * 256 && grad < 5 * 256 ? 255 : + grad < 3 * 256 ? grad - 2 * 256 : 6 * 256 - 1 - grad; + grad += dgrad; + rgrad += drgrad; + if (rgrad >= GRADIENT_SIZE) { + grad++; + rgrad -= GRADIENT_SIZE; + } + if (grad >= GRADIENT_SIZE) + grad -= GRADIENT_SIZE; + } + p = p0; + for (y = height / 8; y > 0; y--) { + memcpy(p+frame->linesize[0], p, 3 * width); + p += frame->linesize[0]; + } + + /* draw digits */ + seg_size = width / 80; + if (seg_size >= 1 && height >= 13 * seg_size) { + int64_t p10decimals = 1; + double time = av_q2d(test->time_base) * test->nb_frame * + pow(10, test->nb_decimals); + if (time >= INT_MAX) + return; + + for (x = 0; x < test->nb_decimals; x++) + p10decimals *= 10; + + second = av_rescale_rnd(test->nb_frame * test->time_base.num, p10decimals, test->time_base.den, AV_ROUND_ZERO); + x = width - (width - seg_size * 64) / 2; + y = (height - seg_size * 13) / 2; + p = data + (x*3 + y * frame->linesize[0]); + for (i = 0; i < 8; i++) { + p -= 3 * 8 * seg_size; + draw_digit(second % 10, p, frame->linesize[0], seg_size); + second /= 10; + if (second == 0) + break; + } + } +} + +static av_cold int test_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = test_fill_picture; + return init(ctx); +} + +static int test_query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE + }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static const AVFilterPad avfilter_vsrc_testsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL } +}; + +AVFilter avfilter_vsrc_testsrc = { + .name = "testsrc", + .description = NULL_IF_CONFIG_SMALL("Generate test pattern."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &testsrc_class, + .init = test_init, + .uninit = uninit, + + .query_formats = test_query_formats, + + .inputs = NULL, + .outputs = avfilter_vsrc_testsrc_outputs, +}; + +#endif /* CONFIG_TESTSRC_FILTER */ + +#if CONFIG_RGBTESTSRC_FILTER + +#define rgbtestsrc_options options +AVFILTER_DEFINE_CLASS(rgbtestsrc); + +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +static void rgbtest_put_pixel(uint8_t *dst, int dst_linesize, + int x, int y, int r, int g, int b, enum AVPixelFormat fmt, + uint8_t rgba_map[4]) +{ + int32_t v; + uint8_t *p; + + switch (fmt) { + case AV_PIX_FMT_BGR444: ((uint16_t*)(dst + y*dst_linesize))[x] = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4); break; + case AV_PIX_FMT_RGB444: ((uint16_t*)(dst + y*dst_linesize))[x] = ((b >> 4) << 8) | ((g >> 4) << 4) | (r >> 4); break; + case AV_PIX_FMT_BGR555: ((uint16_t*)(dst + y*dst_linesize))[x] = ((r>>3)<<10) | ((g>>3)<<5) | (b>>3); break; + case AV_PIX_FMT_RGB555: ((uint16_t*)(dst + y*dst_linesize))[x] = ((b>>3)<<10) | ((g>>3)<<5) | (r>>3); break; + case AV_PIX_FMT_BGR565: ((uint16_t*)(dst + y*dst_linesize))[x] = ((r>>3)<<11) | ((g>>2)<<5) | (b>>3); break; + case AV_PIX_FMT_RGB565: ((uint16_t*)(dst + y*dst_linesize))[x] = ((b>>3)<<11) | ((g>>2)<<5) | (r>>3); break; + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)); + p = dst + 3*x + y*dst_linesize; + AV_WL24(p, v); + break; + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_ARGB: + case AV_PIX_FMT_ABGR: + v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)) + (255 << (rgba_map[A]*8)); + p = dst + 4*x + y*dst_linesize; + AV_WL32(p, v); + break; + } +} + +static void rgbtest_fill_picture(AVFilterContext *ctx, AVFrame *frame) +{ + TestSourceContext *test = ctx->priv; + int x, y, w = frame->width, h = frame->height; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + int c = 256*x/w; + int r = 0, g = 0, b = 0; + + if (3*y < h ) r = c; + else if (3*y < 2*h) g = c; + else b = c; + + rgbtest_put_pixel(frame->data[0], frame->linesize[0], x, y, r, g, b, + ctx->outputs[0]->format, test->rgba_map); + } + } +} + +static av_cold int rgbtest_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->draw_once = 1; + test->fill_picture_fn = rgbtest_fill_picture; + return init(ctx); +} + +static int rgbtest_query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGBA, AV_PIX_FMT_ARGB, AV_PIX_FMT_BGRA, AV_PIX_FMT_ABGR, + AV_PIX_FMT_BGR24, AV_PIX_FMT_RGB24, + AV_PIX_FMT_RGB444, AV_PIX_FMT_BGR444, + AV_PIX_FMT_RGB565, AV_PIX_FMT_BGR565, + AV_PIX_FMT_RGB555, AV_PIX_FMT_BGR555, + AV_PIX_FMT_NONE + }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static int rgbtest_config_props(AVFilterLink *outlink) +{ + TestSourceContext *test = outlink->src->priv; + + ff_fill_rgba_map(test->rgba_map, outlink->format); + return config_props(outlink); +} + +static const AVFilterPad avfilter_vsrc_rgbtestsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = rgbtest_config_props, + }, + { NULL } +}; + +AVFilter avfilter_vsrc_rgbtestsrc = { + .name = "rgbtestsrc", + .description = NULL_IF_CONFIG_SMALL("Generate RGB test pattern."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &rgbtestsrc_class, + .init = rgbtest_init, + .uninit = uninit, + + .query_formats = rgbtest_query_formats, + + .inputs = NULL, + + .outputs = avfilter_vsrc_rgbtestsrc_outputs, +}; + +#endif /* CONFIG_RGBTESTSRC_FILTER */ + +#if CONFIG_SMPTEBARS_FILTER || CONFIG_SMPTEHDBARS_FILTER + +static const uint8_t rainbow[7][4] = { + { 191, 191, 191, 255 }, /* gray */ + { 191, 191, 0, 255 }, /* yellow */ + { 0, 191, 191, 255 }, /* cyan */ + { 0, 191, 0, 255 }, /* green */ + { 191, 0, 191, 255 }, /* magenta */ + { 191, 0, 0, 255 }, /* red */ + { 0, 0, 191, 255 }, /* blue */ +}; + +static const uint8_t wobnair[7][4] = { + { 0, 0, 191, 255 }, /* blue */ + { 19, 19, 19, 255 }, /* 7.5% intensity black */ + { 191, 0, 191, 255 }, /* magenta */ + { 19, 19, 19, 255 }, /* 7.5% intensity black */ + { 0, 191, 191, 255 }, /* cyan */ + { 19, 19, 19, 255 }, /* 7.5% intensity black */ + { 191, 191, 191, 255 }, /* gray */ +}; + +static const uint8_t white[4] = { 255, 255, 255, 255 }; +static const uint8_t black[4] = { 19, 19, 19, 255 }; /* 7.5% intensity black */ + +/* pluge pulses */ +static const uint8_t neg4ire[4] = { 9, 9, 9, 255 }; /* 3.5% intensity black */ +static const uint8_t pos4ire[4] = { 29, 29, 29, 255 }; /* 11.5% intensity black */ + +/* fudged Q/-I */ +static const uint8_t i_pixel[4] = { 0, 68, 130, 255 }; +static const uint8_t q_pixel[4] = { 67, 0, 130, 255 }; + +static const uint8_t gray40[4] = { 102, 102, 102, 255 }; +static const uint8_t gray15[4] = { 38, 38, 38, 255 }; +static const uint8_t cyan[4] = { 0, 255, 255, 255 }; +static const uint8_t yellow[4] = { 255, 255, 0, 255 }; +static const uint8_t blue[4] = { 0, 0, 255, 255 }; +static const uint8_t red[4] = { 255, 0, 0, 255 }; +static const uint8_t black0[4] = { 5, 5, 5, 255 }; +static const uint8_t black2[4] = { 10, 10, 10, 255 }; +static const uint8_t black4[4] = { 15, 15, 15, 255 }; +static const uint8_t neg2[4] = { 0, 0, 0, 255 }; + +static void inline draw_bar(TestSourceContext *test, const uint8_t *color, + unsigned x, unsigned y, unsigned w, unsigned h, + AVFrame *frame) +{ + FFDrawColor draw_color; + + x = FFMIN(x, test->w - 1); + y = FFMIN(y, test->h - 1); + w = FFMIN(w, test->w - x); + h = FFMIN(h, test->h - y); + + av_assert0(x + w <= test->w); + av_assert0(y + h <= test->h); + + ff_draw_color(&test->draw, &draw_color, color); + ff_fill_rectangle(&test->draw, &draw_color, + frame->data, frame->linesize, x, y, w, h); +} + +static int smptebars_query_formats(AVFilterContext *ctx) +{ + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + return 0; +} + +static int smptebars_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TestSourceContext *test = ctx->priv; + + ff_draw_init(&test->draw, outlink->format, 0); + + return config_props(outlink); +} + +static const AVFilterPad smptebars_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = smptebars_config_props, + }, + { NULL } +}; + +#if CONFIG_SMPTEBARS_FILTER + +#define smptebars_options options +AVFILTER_DEFINE_CLASS(smptebars); + +static void smptebars_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + int r_w, r_h, w_h, p_w, p_h, i, tmp, x = 0; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); + + r_w = FFALIGN((test->w + 6) / 7, 1 << pixdesc->log2_chroma_w); + r_h = FFALIGN(test->h * 2 / 3, 1 << pixdesc->log2_chroma_h); + w_h = FFALIGN(test->h * 3 / 4 - r_h, 1 << pixdesc->log2_chroma_h); + p_w = FFALIGN(r_w * 5 / 4, 1 << pixdesc->log2_chroma_w); + p_h = test->h - w_h - r_h; + + for (i = 0; i < 7; i++) { + draw_bar(test, rainbow[i], x, 0, r_w, r_h, picref); + draw_bar(test, wobnair[i], x, r_h, r_w, w_h, picref); + x += r_w; + } + x = 0; + draw_bar(test, i_pixel, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + draw_bar(test, white, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + draw_bar(test, q_pixel, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + tmp = FFALIGN(5 * r_w - x, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + tmp = FFALIGN(r_w / 3, 1 << pixdesc->log2_chroma_w); + draw_bar(test, neg4ire, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, black, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, pos4ire, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, black, x, r_h + w_h, test->w - x, p_h, picref); +} + +static av_cold int smptebars_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = smptebars_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +AVFilter avfilter_vsrc_smptebars = { + .name = "smptebars", + .description = NULL_IF_CONFIG_SMALL("Generate SMPTE color bars."), + .priv_size = sizeof(TestSourceContext), + .init = smptebars_init, + .uninit = uninit, + + .query_formats = smptebars_query_formats, + .inputs = NULL, + .outputs = smptebars_outputs, + .priv_class = &smptebars_class, +}; + +#endif /* CONFIG_SMPTEBARS_FILTER */ + +#if CONFIG_SMPTEHDBARS_FILTER + +#define smptehdbars_options options +AVFILTER_DEFINE_CLASS(smptehdbars); + +static void smptehdbars_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + int d_w, r_w, r_h, l_w, i, tmp, x = 0, y = 0; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); + + d_w = FFALIGN(test->w / 8, 1 << pixdesc->log2_chroma_w); + r_h = FFALIGN(test->h * 7 / 12, 1 << pixdesc->log2_chroma_h); + draw_bar(test, gray40, x, 0, d_w, r_h, picref); + x += d_w; + + r_w = FFALIGN((((test->w + 3) / 4) * 3) / 7, 1 << pixdesc->log2_chroma_w); + for (i = 0; i < 7; i++) { + draw_bar(test, rainbow[i], x, 0, r_w, r_h, picref); + x += r_w; + } + draw_bar(test, gray40, x, 0, test->w - x, r_h, picref); + y = r_h; + r_h = FFALIGN(test->h / 12, 1 << pixdesc->log2_chroma_h); + draw_bar(test, cyan, 0, y, d_w, r_h, picref); + x = d_w; + draw_bar(test, i_pixel, x, y, r_w, r_h, picref); + x += r_w; + tmp = r_w * 6; + draw_bar(test, rainbow[0], x, y, tmp, r_h, picref); + x += tmp; + l_w = x; + draw_bar(test, blue, x, y, test->w - x, r_h, picref); + y += r_h; + draw_bar(test, yellow, 0, y, d_w, r_h, picref); + x = d_w; + draw_bar(test, q_pixel, x, y, r_w, r_h, picref); + x += r_w; + + for (i = 0; i < tmp; i += 1 << pixdesc->log2_chroma_w) { + uint8_t yramp[4] = {0}; + + yramp[0] = + yramp[1] = + yramp[2] = i * 255 / tmp; + yramp[3] = 255; + + draw_bar(test, yramp, x, y, 1 << pixdesc->log2_chroma_w, r_h, picref); + x += 1 << pixdesc->log2_chroma_w; + } + draw_bar(test, red, x, y, test->w - x, r_h, picref); + y += r_h; + draw_bar(test, gray15, 0, y, d_w, test->h - y, picref); + x = d_w; + tmp = FFALIGN(r_w * 3 / 2, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w * 2, 1 << pixdesc->log2_chroma_w); + draw_bar(test, white, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w * 5 / 6, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w / 3, 1 << pixdesc->log2_chroma_w); + draw_bar(test, neg2, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black2, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black4, x, y, tmp, test->h - y, picref); + x += tmp; + r_w = l_w - x; + draw_bar(test, black0, x, y, r_w, test->h - y, picref); + x += r_w; + draw_bar(test, gray15, x, y, test->w - x, test->h - y, picref); +} + +static av_cold int smptehdbars_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = smptehdbars_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +AVFilter avfilter_vsrc_smptehdbars = { + .name = "smptehdbars", + .description = NULL_IF_CONFIG_SMALL("Generate SMPTE HD color bars."), + .priv_size = sizeof(TestSourceContext), + .init = smptehdbars_init, + .uninit = uninit, + + .query_formats = smptebars_query_formats, + .inputs = NULL, + .outputs = smptebars_outputs, + .priv_class = &smptehdbars_class, +}; + +#endif /* CONFIG_SMPTEHDBARS_FILTER */ +#endif /* CONFIG_SMPTEBARS_FILTER || CONFIG_SMPTEHDBARS_FILTER */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_testsrc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/vsrc_testsrc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavfilter/vsrc_testsrc.o libavfilter/vsrc_testsrc.o: \ + libavfilter/vsrc_testsrc.c libavutil/avassert.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/common.h libavutil/opt.h libavutil/samplefmt.h \ + libavutil/imgutils.h libavutil/pixdesc.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/parseutils.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/log.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavfilter/version.h libavutil/avutil.h libavfilter/drawutils.h \ + libavfilter/formats.h libavfilter/internal.h \ + libavfilter/avfiltergraph.h libavfilter/video.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/vsrc_testsrc.o Binary file ffmpeg/libavfilter/vsrc_testsrc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/Makefile Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,8 @@ +OBJS-$(CONFIG_GRADFUN_FILTER) += x86/vf_gradfun.o +OBJS-$(CONFIG_HQDN3D_FILTER) += x86/vf_hqdn3d_init.o +OBJS-$(CONFIG_VOLUME_FILTER) += x86/af_volume_init.o +OBJS-$(CONFIG_YADIF_FILTER) += x86/vf_yadif_init.o + +YASM-OBJS-$(CONFIG_HQDN3D_FILTER) += x86/vf_hqdn3d.o +YASM-OBJS-$(CONFIG_VOLUME_FILTER) += x86/af_volume.o +YASM-OBJS-$(CONFIG_YADIF_FILTER) += x86/vf_yadif.o x86/yadif-16.o x86/yadif-10.o diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/af_volume.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/af_volume.asm Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,140 @@ +;***************************************************************************** +;* x86-optimized functions for volume filter +;* Copyright (c) 2012 Justin Ruggles +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General Public +;* License along with FFmpeg; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA 32 + +pd_1_256: times 4 dq 0x3F70000000000000 +pd_int32_max: times 4 dq 0x41DFFFFFFFC00000 +pw_1: times 8 dw 1 +pw_128: times 8 dw 128 +pq_128: times 2 dq 128 + +SECTION_TEXT + +;------------------------------------------------------------------------------ +; void ff_scale_samples_s16(uint8_t *dst, const uint8_t *src, int len, +; int volume) +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal scale_samples_s16, 4,4,4, dst, src, len, volume + movd m0, volumem + pshuflw m0, m0, 0 + punpcklwd m0, [pw_1] + mova m1, [pw_128] + lea lenq, [lend*2-mmsize] +.loop: + ; dst[i] = av_clip_int16((src[i] * volume + 128) >> 8); + mova m2, [srcq+lenq] + punpcklwd m3, m2, m1 + punpckhwd m2, m1 + pmaddwd m3, m0 + pmaddwd m2, m0 + psrad m3, 8 + psrad m2, 8 + packssdw m3, m2 + mova [dstq+lenq], m3 + sub lenq, mmsize + jge .loop + REP_RET + +;------------------------------------------------------------------------------ +; void ff_scale_samples_s32(uint8_t *dst, const uint8_t *src, int len, +; int volume) +;------------------------------------------------------------------------------ + +%macro SCALE_SAMPLES_S32 0 +cglobal scale_samples_s32, 4,4,4, dst, src, len, volume +%if ARCH_X86_32 && cpuflag(avx) + vbroadcastss xmm2, volumem +%else + movd xmm2, volumed + pshufd xmm2, xmm2, 0 +%endif + CVTDQ2PD m2, xmm2 + mulpd m2, m2, [pd_1_256] + mova m3, [pd_int32_max] + lea lenq, [lend*4-mmsize] +.loop: + CVTDQ2PD m0, [srcq+lenq ] + CVTDQ2PD m1, [srcq+lenq+mmsize/2] + mulpd m0, m0, m2 + mulpd m1, m1, m2 + minpd m0, m0, m3 + minpd m1, m1, m3 + cvtpd2dq xmm0, m0 + cvtpd2dq xmm1, m1 +%if cpuflag(avx) + vmovdqa [dstq+lenq ], xmm0 + vmovdqa [dstq+lenq+mmsize/2], xmm1 +%else + movq [dstq+lenq ], xmm0 + movq [dstq+lenq+mmsize/2], xmm1 +%endif + sub lenq, mmsize + jge .loop + REP_RET +%endmacro + +INIT_XMM sse2 +%define CVTDQ2PD cvtdq2pd +SCALE_SAMPLES_S32 +%if HAVE_AVX_EXTERNAL +%define CVTDQ2PD vcvtdq2pd +INIT_YMM avx +SCALE_SAMPLES_S32 +%endif +%undef CVTDQ2PD + +; NOTE: This is not bit-identical with the C version because it clips to +; [-INT_MAX, INT_MAX] instead of [INT_MIN, INT_MAX] + +INIT_XMM ssse3, atom +cglobal scale_samples_s32, 4,4,8, dst, src, len, volume + movd m4, volumem + pshufd m4, m4, 0 + mova m5, [pq_128] + pxor m6, m6 + lea lenq, [lend*4-mmsize] +.loop: + ; src[i] = av_clipl_int32((src[i] * volume + 128) >> 8); + mova m7, [srcq+lenq] + pabsd m3, m7 + pshufd m0, m3, q0100 + pshufd m1, m3, q0302 + pmuludq m0, m4 + pmuludq m1, m4 + paddq m0, m5 + paddq m1, m5 + psrlq m0, 7 + psrlq m1, 7 + shufps m2, m0, m1, q3131 + shufps m0, m0, m1, q2020 + pcmpgtd m2, m6 + por m0, m2 + psrld m0, 1 + psignd m0, m7 + mova [dstq+lenq], m0 + sub lenq, mmsize + jge .loop + REP_RET diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/af_volume_init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/af_volume_init.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,59 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "libavutil/cpu.h" +#include "libavutil/samplefmt.h" +#include "libavutil/x86/cpu.h" +#include "libavfilter/af_volume.h" + +void ff_scale_samples_s16_sse2(uint8_t *dst, const uint8_t *src, int len, + int volume); + +void ff_scale_samples_s32_sse2(uint8_t *dst, const uint8_t *src, int len, + int volume); +void ff_scale_samples_s32_ssse3_atom(uint8_t *dst, const uint8_t *src, int len, + int volume); +void ff_scale_samples_s32_avx(uint8_t *dst, const uint8_t *src, int len, + int volume); + +void ff_volume_init_x86(VolumeContext *vol) +{ + int mm_flags = av_get_cpu_flags(); + enum AVSampleFormat sample_fmt = av_get_packed_sample_fmt(vol->sample_fmt); + + if (sample_fmt == AV_SAMPLE_FMT_S16) { + if (EXTERNAL_SSE2(mm_flags) && vol->volume_i < 32768) { + vol->scale_samples = ff_scale_samples_s16_sse2; + vol->samples_align = 8; + } + } else if (sample_fmt == AV_SAMPLE_FMT_S32) { + if (EXTERNAL_SSE2(mm_flags)) { + vol->scale_samples = ff_scale_samples_s32_sse2; + vol->samples_align = 4; + } + if (EXTERNAL_SSSE3(mm_flags) && mm_flags & AV_CPU_FLAG_ATOM) { + vol->scale_samples = ff_scale_samples_s32_ssse3_atom; + vol->samples_align = 4; + } + if (EXTERNAL_AVX(mm_flags)) { + vol->scale_samples = ff_scale_samples_s32_avx; + vol->samples_align = 8; + } + } +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/af_volume_init.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/af_volume_init.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,13 @@ +libavfilter/x86/af_volume_init.o libavfilter/x86/af_volume_init.o: \ + libavfilter/x86/af_volume_init.c config.h libavutil/cpu.h \ + libavutil/attributes.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/x86/cpu.h \ + libavfilter/af_volume.h libavutil/common.h libavutil/float_dsp.h \ + libavutil/opt.h libavutil/samplefmt.h libavutil/samplefmt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/af_volume_init.o Binary file ffmpeg/libavfilter/x86/af_volume_init.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_gradfun.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_gradfun.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2009 Loren Merritt + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavfilter/gradfun.h" + +#if HAVE_INLINE_ASM + +DECLARE_ALIGNED(16, static const uint16_t, pw_7f)[8] = {0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F}; +DECLARE_ALIGNED(16, static const uint16_t, pw_ff)[8] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + +#if HAVE_MMXEXT_INLINE +static void gradfun_filter_line_mmxext(uint8_t *dst, const uint8_t *src, const uint16_t *dc, + int width, int thresh, + const uint16_t *dithers) +{ + intptr_t x; + if (width & 3) { + x = width & ~3; + ff_gradfun_filter_line_c(dst + x, src + x, dc + x / 2, width - x, thresh, dithers); + width = x; + } + x = -width; + __asm__ volatile( + "movd %4, %%mm5 \n" + "pxor %%mm7, %%mm7 \n" + "pshufw $0, %%mm5, %%mm5 \n" + "movq %6, %%mm6 \n" + "movq (%5), %%mm3 \n" + "movq 8(%5), %%mm4 \n" + + "1: \n" + "movd (%2,%0), %%mm0 \n" + "movd (%3,%0), %%mm1 \n" + "punpcklbw %%mm7, %%mm0 \n" + "punpcklwd %%mm1, %%mm1 \n" + "psllw $7, %%mm0 \n" + "pxor %%mm2, %%mm2 \n" + "psubw %%mm0, %%mm1 \n" // delta = dc - pix + "psubw %%mm1, %%mm2 \n" + "pmaxsw %%mm1, %%mm2 \n" + "pmulhuw %%mm5, %%mm2 \n" // m = abs(delta) * thresh >> 16 + "psubw %%mm6, %%mm2 \n" + "pminsw %%mm7, %%mm2 \n" // m = -max(0, 127-m) + "pmullw %%mm2, %%mm2 \n" + "paddw %%mm3, %%mm0 \n" // pix += dither + "psllw $2, %%mm1 \n" // m = m*m*delta >> 14 + "pmulhw %%mm2, %%mm1 \n" + "paddw %%mm1, %%mm0 \n" // pix += m + "psraw $7, %%mm0 \n" + "packuswb %%mm0, %%mm0 \n" + "movd %%mm0, (%1,%0) \n" // dst = clip(pix>>7) + "add $4, %0 \n" + "jnl 2f \n" + + "movd (%2,%0), %%mm0 \n" + "movd (%3,%0), %%mm1 \n" + "punpcklbw %%mm7, %%mm0 \n" + "punpcklwd %%mm1, %%mm1 \n" + "psllw $7, %%mm0 \n" + "pxor %%mm2, %%mm2 \n" + "psubw %%mm0, %%mm1 \n" // delta = dc - pix + "psubw %%mm1, %%mm2 \n" + "pmaxsw %%mm1, %%mm2 \n" + "pmulhuw %%mm5, %%mm2 \n" // m = abs(delta) * thresh >> 16 + "psubw %%mm6, %%mm2 \n" + "pminsw %%mm7, %%mm2 \n" // m = -max(0, 127-m) + "pmullw %%mm2, %%mm2 \n" + "paddw %%mm4, %%mm0 \n" // pix += dither + "psllw $2, %%mm1 \n" // m = m*m*delta >> 14 + "pmulhw %%mm2, %%mm1 \n" + "paddw %%mm1, %%mm0 \n" // pix += m + "psraw $7, %%mm0 \n" + "packuswb %%mm0, %%mm0 \n" + "movd %%mm0, (%1,%0) \n" // dst = clip(pix>>7) + "add $4, %0 \n" + "jl 1b \n" + + "2: \n" + "emms \n" + :"+r"(x) + :"r"(dst+width), "r"(src+width), "r"(dc+width/2), + "rm"(thresh), "r"(dithers), "m"(*pw_7f) + :"memory" + ); +} +#endif + +#if HAVE_SSSE3_INLINE +static void gradfun_filter_line_ssse3(uint8_t *dst, const uint8_t *src, const uint16_t *dc, int width, int thresh, const uint16_t *dithers) +{ + intptr_t x; + if (width & 7) { + // could be 10% faster if I somehow eliminated this + x = width & ~7; + ff_gradfun_filter_line_c(dst + x, src + x, dc + x / 2, width - x, thresh, dithers); + width = x; + } + x = -width; + __asm__ volatile( + "movd %4, %%xmm5 \n" + "pxor %%xmm7, %%xmm7 \n" + "pshuflw $0,%%xmm5, %%xmm5 \n" + "movdqa %6, %%xmm6 \n" + "punpcklqdq %%xmm5, %%xmm5 \n" + "movdqa %5, %%xmm4 \n" + "1: \n" + "movq (%2,%0), %%xmm0 \n" + "movq (%3,%0), %%xmm1 \n" + "punpcklbw %%xmm7, %%xmm0 \n" + "punpcklwd %%xmm1, %%xmm1 \n" + "psllw $7, %%xmm0 \n" + "psubw %%xmm0, %%xmm1 \n" // delta = dc - pix + "pabsw %%xmm1, %%xmm2 \n" + "pmulhuw %%xmm5, %%xmm2 \n" // m = abs(delta) * thresh >> 16 + "psubw %%xmm6, %%xmm2 \n" + "pminsw %%xmm7, %%xmm2 \n" // m = -max(0, 127-m) + "pmullw %%xmm2, %%xmm2 \n" + "psllw $2, %%xmm1 \n" + "paddw %%xmm4, %%xmm0 \n" // pix += dither + "pmulhw %%xmm2, %%xmm1 \n" // m = m*m*delta >> 14 + "paddw %%xmm1, %%xmm0 \n" // pix += m + "psraw $7, %%xmm0 \n" + "packuswb %%xmm0, %%xmm0 \n" + "movq %%xmm0, (%1,%0) \n" // dst = clip(pix>>7) + "add $8, %0 \n" + "jl 1b \n" + :"+&r"(x) + :"r"(dst+width), "r"(src+width), "r"(dc+width/2), + "rm"(thresh), "m"(*dithers), "m"(*pw_7f) + :"memory" + ); +} +#endif /* HAVE_SSSE3_INLINE */ + +#if HAVE_SSE2_INLINE +static void gradfun_blur_line_sse2(uint16_t *dc, uint16_t *buf, const uint16_t *buf1, const uint8_t *src, int src_linesize, int width) +{ +#define BLURV(load)\ + intptr_t x = -2*width;\ + __asm__ volatile(\ + "movdqa %6, %%xmm7 \n"\ + "1: \n"\ + load" (%4,%0), %%xmm0 \n"\ + load" (%5,%0), %%xmm1 \n"\ + "movdqa %%xmm0, %%xmm2 \n"\ + "movdqa %%xmm1, %%xmm3 \n"\ + "psrlw $8, %%xmm0 \n"\ + "psrlw $8, %%xmm1 \n"\ + "pand %%xmm7, %%xmm2 \n"\ + "pand %%xmm7, %%xmm3 \n"\ + "paddw %%xmm1, %%xmm0 \n"\ + "paddw %%xmm3, %%xmm2 \n"\ + "paddw %%xmm2, %%xmm0 \n"\ + "paddw (%2,%0), %%xmm0 \n"\ + "movdqa (%1,%0), %%xmm1 \n"\ + "movdqa %%xmm0, (%1,%0) \n"\ + "psubw %%xmm1, %%xmm0 \n"\ + "movdqa %%xmm0, (%3,%0) \n"\ + "add $16, %0 \n"\ + "jl 1b \n"\ + :"+&r"(x)\ + :"r"(buf+width),\ + "r"(buf1+width),\ + "r"(dc+width),\ + "r"(src+width*2),\ + "r"(src+width*2+src_linesize),\ + "m"(*pw_ff)\ + :"memory"\ + ); + if (((intptr_t) src | src_linesize) & 15) { + BLURV("movdqu"); + } else { + BLURV("movdqa"); + } +} +#endif /* HAVE_SSE2_INLINE */ + +#endif /* HAVE_INLINE_ASM */ + +av_cold void ff_gradfun_init_x86(GradFunContext *gf) +{ + int cpu_flags = av_get_cpu_flags(); + +#if HAVE_MMXEXT_INLINE + if (cpu_flags & AV_CPU_FLAG_MMXEXT) + gf->filter_line = gradfun_filter_line_mmxext; +#endif +#if HAVE_SSSE3_INLINE + if (cpu_flags & AV_CPU_FLAG_SSSE3) + gf->filter_line = gradfun_filter_line_ssse3; +#endif +#if HAVE_SSE2_INLINE + if (cpu_flags & AV_CPU_FLAG_SSE2) + gf->blur_line = gradfun_blur_line_sse2; +#endif +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_gradfun.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_gradfun.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,16 @@ +libavfilter/x86/vf_gradfun.o libavfilter/x86/vf_gradfun.o: \ + libavfilter/x86/vf_gradfun.c libavutil/attributes.h libavutil/cpu.h \ + libavutil/attributes.h libavutil/mem.h libavutil/error.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/x86/asm.h \ + libavfilter/gradfun.h libavfilter/avfilter.h libavutil/avutil.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/samplefmt.h libavutil/pixfmt.h \ + libavutil/rational.h libavfilter/version.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_gradfun.o Binary file ffmpeg/libavfilter/x86/vf_gradfun.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_hqdn3d.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_hqdn3d.asm Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,106 @@ +;****************************************************************************** +;* Copyright (c) 2012 Loren Merritt +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General Public +;* License along with FFmpeg; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" + +SECTION .text + +%macro LOWPASS 3 ; prevsample, cursample, lut + sub %1q, %2q +%if lut_bits != 8 + sar %1q, 8-lut_bits +%endif + movsx %1d, word [%3q+%1q*2] + add %1d, %2d +%endmacro + +%macro LOAD 3 ; dstreg, x, bitdepth +%if %3 == 8 + movzx %1, byte [srcq+%2] +%else + movzx %1, word [srcq+(%2)*2] +%endif +%if %3 != 16 + shl %1, 16-%3 + add %1, (1<<(15-%3))-1 +%endif +%endmacro + +%macro HQDN3D_ROW 1 ; bitdepth +%if ARCH_X86_64 +cglobal hqdn3d_row_%1_x86, 7,10,0, src, dst, lineant, frameant, width, spatial, temporal, pixelant, t0, t1 +%else +cglobal hqdn3d_row_%1_x86, 7,7,0, src, dst, lineant, frameant, width, spatial, temporal +%endif + %assign bytedepth (%1+7)>>3 + %assign lut_bits 4+4*(%1/16) + dec widthq + lea srcq, [srcq+widthq*bytedepth] + lea dstq, [dstq+widthq*bytedepth] + lea frameantq, [frameantq+widthq*2] + lea lineantq, [lineantq+widthq*2] + neg widthq + %define xq widthq +%if ARCH_X86_32 + mov dstmp, dstq + mov srcmp, srcq + mov frameantmp, frameantq + mov lineantmp, lineantq + %define dstq r0 + %define frameantq r0 + %define lineantq r0 + %define pixelantq r1 + %define pixelantd r1d + DECLARE_REG_TMP 2,3 +%endif + LOAD pixelantd, xq, %1 +ALIGN 16 +.loop: + movifnidn srcq, srcmp + LOAD t0d, xq+1, %1 ; skip on the last iteration to avoid overread +.loop2: + movifnidn lineantq, lineantmp + movzx t1d, word [lineantq+xq*2] + LOWPASS t1, pixelant, spatial + mov [lineantq+xq*2], t1w + LOWPASS pixelant, t0, spatial + movifnidn frameantq, frameantmp + movzx t0d, word [frameantq+xq*2] + LOWPASS t0, t1, temporal + mov [frameantq+xq*2], t0w + movifnidn dstq, dstmp +%if %1 != 16 + shr t0d, 16-%1 ; could eliminate this by storing from t0h, but only with some contraints on register allocation +%endif +%if %1 == 8 + mov [dstq+xq], t0b +%else + mov [dstq+xq*2], t0w +%endif + inc xq + jl .loop + je .loop2 + REP_RET +%endmacro ; HQDN3D_ROW + +HQDN3D_ROW 8 +HQDN3D_ROW 9 +HQDN3D_ROW 10 +HQDN3D_ROW 16 diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_hqdn3d_init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_hqdn3d_init.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Loren Merritt + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include "libavutil/attributes.h" +#include "libavfilter/vf_hqdn3d.h" +#include "config.h" + +void ff_hqdn3d_row_8_x86(uint8_t *src, uint8_t *dst, uint16_t *line_ant, uint16_t *frame_ant, ptrdiff_t w, int16_t *spatial, int16_t *temporal); +void ff_hqdn3d_row_9_x86(uint8_t *src, uint8_t *dst, uint16_t *line_ant, uint16_t *frame_ant, ptrdiff_t w, int16_t *spatial, int16_t *temporal); +void ff_hqdn3d_row_10_x86(uint8_t *src, uint8_t *dst, uint16_t *line_ant, uint16_t *frame_ant, ptrdiff_t w, int16_t *spatial, int16_t *temporal); +void ff_hqdn3d_row_16_x86(uint8_t *src, uint8_t *dst, uint16_t *line_ant, uint16_t *frame_ant, ptrdiff_t w, int16_t *spatial, int16_t *temporal); + +av_cold void ff_hqdn3d_init_x86(HQDN3DContext *hqdn3d) +{ +#if HAVE_YASM + hqdn3d->denoise_row[ 8] = ff_hqdn3d_row_8_x86; + hqdn3d->denoise_row[ 9] = ff_hqdn3d_row_9_x86; + hqdn3d->denoise_row[10] = ff_hqdn3d_row_10_x86; + hqdn3d->denoise_row[16] = ff_hqdn3d_row_16_x86; +#endif +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_yadif.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_yadif.asm Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,252 @@ +;***************************************************************************** +;* x86-optimized functions for yadif filter +;* +;* Copyright (C) 2006 Michael Niedermayer +;* Copyright (c) 2013 Daniel Kang +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License along +;* with FFmpeg; if not, write to the Free Software Foundation, Inc., +;* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA + +pb_1: times 16 db 1 +pw_1: times 8 dw 1 + +SECTION .text + +%macro CHECK 2 + movu m2, [curq+t1+%1] + movu m3, [curq+t0+%2] + mova m4, m2 + mova m5, m2 + pxor m4, m3 + pavgb m5, m3 + pand m4, [pb_1] + psubusb m5, m4 +%if mmsize == 16 + psrldq m5, 1 +%else + psrlq m5, 8 +%endif + punpcklbw m5, m7 + mova m4, m2 + psubusb m2, m3 + psubusb m3, m4 + pmaxub m2, m3 + mova m3, m2 + mova m4, m2 +%if mmsize == 16 + psrldq m3, 1 + psrldq m4, 2 +%else + psrlq m3, 8 + psrlq m4, 16 +%endif + punpcklbw m2, m7 + punpcklbw m3, m7 + punpcklbw m4, m7 + paddw m2, m3 + paddw m2, m4 +%endmacro + +%macro CHECK1 0 + mova m3, m0 + pcmpgtw m3, m2 + pminsw m0, m2 + mova m6, m3 + pand m5, m3 + pandn m3, m1 + por m3, m5 + mova m1, m3 +%endmacro + +%macro CHECK2 0 + paddw m6, [pw_1] + psllw m6, 14 + paddsw m2, m6 + mova m3, m0 + pcmpgtw m3, m2 + pminsw m0, m2 + pand m5, m3 + pandn m3, m1 + por m3, m5 + mova m1, m3 +%endmacro + +%macro LOAD 2 + movh %1, %2 + punpcklbw %1, m7 +%endmacro + +%macro FILTER 3 +.loop%1: + pxor m7, m7 + LOAD m0, [curq+t1] + LOAD m1, [curq+t0] + LOAD m2, [%2] + LOAD m3, [%3] + mova m4, m3 + paddw m3, m2 + psraw m3, 1 + mova [rsp+ 0], m0 + mova [rsp+16], m3 + mova [rsp+32], m1 + psubw m2, m4 + ABS1 m2, m4 + LOAD m3, [prevq+t1] + LOAD m4, [prevq+t0] + psubw m3, m0 + psubw m4, m1 + ABS1 m3, m5 + ABS1 m4, m5 + paddw m3, m4 + psrlw m2, 1 + psrlw m3, 1 + pmaxsw m2, m3 + LOAD m3, [nextq+t1] + LOAD m4, [nextq+t0] + psubw m3, m0 + psubw m4, m1 + ABS1 m3, m5 + ABS1 m4, m5 + paddw m3, m4 + psrlw m3, 1 + pmaxsw m2, m3 + mova [rsp+48], m2 + + paddw m1, m0 + paddw m0, m0 + psubw m0, m1 + psrlw m1, 1 + ABS1 m0, m2 + + movu m2, [curq+t1-1] + movu m3, [curq+t0-1] + mova m4, m2 + psubusb m2, m3 + psubusb m3, m4 + pmaxub m2, m3 +%if mmsize == 16 + mova m3, m2 + psrldq m3, 2 +%else + pshufw m3, m2, q0021 +%endif + punpcklbw m2, m7 + punpcklbw m3, m7 + paddw m0, m2 + paddw m0, m3 + psubw m0, [pw_1] + + CHECK -2, 0 + CHECK1 + CHECK -3, 1 + CHECK2 + CHECK 0, -2 + CHECK1 + CHECK 1, -3 + CHECK2 + + mova m6, [rsp+48] + cmp DWORD r8m, 2 + jge .end%1 + LOAD m2, [%2+t1*2] + LOAD m4, [%3+t1*2] + LOAD m3, [%2+t0*2] + LOAD m5, [%3+t0*2] + paddw m2, m4 + paddw m3, m5 + psrlw m2, 1 + psrlw m3, 1 + mova m4, [rsp+ 0] + mova m5, [rsp+16] + mova m7, [rsp+32] + psubw m2, m4 + psubw m3, m7 + mova m0, m5 + psubw m5, m4 + psubw m0, m7 + mova m4, m2 + pminsw m2, m3 + pmaxsw m3, m4 + pmaxsw m2, m5 + pminsw m3, m5 + pmaxsw m2, m0 + pminsw m3, m0 + pxor m4, m4 + pmaxsw m6, m3 + psubw m4, m2 + pmaxsw m6, m4 + +.end%1: + mova m2, [rsp+16] + mova m3, m2 + psubw m2, m6 + paddw m3, m6 + pmaxsw m1, m2 + pminsw m1, m3 + packuswb m1, m1 + + movh [dstq], m1 + add dstq, mmsize/2 + add prevq, mmsize/2 + add curq, mmsize/2 + add nextq, mmsize/2 + sub DWORD r4m, mmsize/2 + jg .loop%1 +%endmacro + +%macro YADIF 0 +%if ARCH_X86_32 +cglobal yadif_filter_line, 4, 6, 8, 80, dst, prev, cur, next, w, prefs, \ + mrefs, parity, mode +%else +cglobal yadif_filter_line, 4, 7, 8, 80, dst, prev, cur, next, w, prefs, \ + mrefs, parity, mode +%endif +%if ARCH_X86_32 + mov r4, r5mp + mov r5, r6mp + DECLARE_REG_TMP 4,5 +%else + movsxd r5, DWORD r5m + movsxd r6, DWORD r6m + DECLARE_REG_TMP 5,6 +%endif + + cmp DWORD paritym, 0 + je .parity0 + FILTER 1, prevq, curq + jmp .ret + +.parity0: + FILTER 0, curq, nextq + +.ret: + RET +%endmacro + +INIT_XMM ssse3 +YADIF +INIT_XMM sse2 +YADIF +%if ARCH_X86_32 +INIT_MMX mmxext +YADIF +%endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/vf_yadif_init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/vf_yadif_init.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavcodec/x86/dsputil_mmx.h" +#include "libavfilter/yadif.h" + +void ff_yadif_filter_line_mmxext(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_sse2(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_ssse3(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); + +void ff_yadif_filter_line_16bit_mmxext(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_16bit_sse2(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_16bit_ssse3(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_16bit_sse4(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); + +void ff_yadif_filter_line_10bit_mmxext(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_10bit_sse2(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); +void ff_yadif_filter_line_10bit_ssse3(void *dst, void *prev, void *cur, + void *next, int w, int prefs, + int mrefs, int parity, int mode); + +av_cold void ff_yadif_init_x86(YADIFContext *yadif) +{ + int cpu_flags = av_get_cpu_flags(); + int bit_depth = (!yadif->csp) ? 8 + : yadif->csp->comp[0].depth_minus1 + 1; + +#if HAVE_YASM + if (bit_depth >= 15) { +#if ARCH_X86_32 + if (EXTERNAL_MMXEXT(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_16bit_mmxext; +#endif /* ARCH_X86_32 */ + if (EXTERNAL_SSE2(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_16bit_sse2; + if (EXTERNAL_SSSE3(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_16bit_ssse3; + if (EXTERNAL_SSE4(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_16bit_sse4; + } else if ( bit_depth >= 9 && bit_depth <= 14) { +#if ARCH_X86_32 + if (EXTERNAL_MMXEXT(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_10bit_mmxext; +#endif /* ARCH_X86_32 */ + if (EXTERNAL_SSE2(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_10bit_sse2; + if (EXTERNAL_SSSE3(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_10bit_ssse3; + } else { +#if ARCH_X86_32 + if (EXTERNAL_MMXEXT(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_mmxext; +#endif /* ARCH_X86_32 */ + if (EXTERNAL_SSE2(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_sse2; + if (EXTERNAL_SSSE3(cpu_flags)) + yadif->filter_line = ff_yadif_filter_line_ssse3; + } +#endif /* HAVE_YASM */ +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/yadif-10.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/yadif-10.asm Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,282 @@ +;***************************************************************************** +;* x86-optimized functions for yadif filter +;* +;* Copyright (C) 2006 Michael Niedermayer +;* Copyright (c) 2013 Daniel Kang +;* Copyright (c) 2011-2013 James Darnley +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License along +;* with FFmpeg; if not, write to the Free Software Foundation, Inc., +;* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA + +pw_1: times 8 dw 1 + +SECTION .text + +%macro PABS 2 +%if cpuflag(ssse3) + pabsw %1, %1 +%else + pxor %2, %2 + pcmpgtw %2, %1 + pxor %1, %2 + psubw %1, %2 +%endif +%endmacro + +%macro PMAXUW 2 +%if cpuflag(sse4) + pmaxuw %1, %2 +%else + psubusw %1, %2 + paddusw %1, %2 +%endif +%endmacro + +%macro CHECK 2 + movu m2, [curq+t1+%1*2] + movu m3, [curq+t0+%2*2] + mova m4, m2 + mova m5, m2 + pxor m4, m3 + pavgw m5, m3 + pand m4, [pw_1] + psubusw m5, m4 +%if mmsize == 16 + psrldq m5, 2 +%else + psrlq m5, 16 +%endif + mova m4, m2 + psubusw m2, m3 + psubusw m3, m4 + PMAXUW m2, m3 + mova m3, m2 + mova m4, m2 +%if mmsize == 16 + psrldq m3, 2 + psrldq m4, 4 +%else + psrlq m3, 16 + psrlq m4, 32 +%endif + paddw m2, m3 + paddw m2, m4 +%endmacro + +%macro CHECK1 0 + mova m3, m0 + pcmpgtw m3, m2 + pminsw m0, m2 + mova m6, m3 + pand m5, m3 + pandn m3, m1 + por m3, m5 + mova m1, m3 +%endmacro + +; %macro CHECK2 0 +; paddw m6, [pw_1] +; psllw m6, 14 +; paddsw m2, m6 +; mova m3, m0 +; pcmpgtw m3, m2 +; pminsw m0, m2 +; pand m5, m3 +; pandn m3, m1 +; por m3, m5 +; mova m1, m3 +; %endmacro + +; This version of CHECK2 is required for 14-bit samples. The left-shift trick +; in the old code is not large enough to correctly select pixels or scores. + +%macro CHECK2 0 + mova m3, m0 + pcmpgtw m0, m2 + pand m0, m6 + mova m6, m0 + pand m5, m6 + pand m2, m0 + pandn m6, m1 + pandn m0, m3 + por m6, m5 + por m0, m2 + mova m1, m6 +%endmacro + +%macro LOAD 2 + movu %1, %2 +%endmacro + +%macro FILTER 3 +.loop%1: + pxor m7, m7 + LOAD m0, [curq+t1] + LOAD m1, [curq+t0] + LOAD m2, [%2] + LOAD m3, [%3] + mova m4, m3 + paddw m3, m2 + psraw m3, 1 + mova [rsp+ 0], m0 + mova [rsp+16], m3 + mova [rsp+32], m1 + psubw m2, m4 + PABS m2, m4 + LOAD m3, [prevq+t1] + LOAD m4, [prevq+t0] + psubw m3, m0 + psubw m4, m1 + PABS m3, m5 + PABS m4, m5 + paddw m3, m4 + psrlw m2, 1 + psrlw m3, 1 + pmaxsw m2, m3 + LOAD m3, [nextq+t1] + LOAD m4, [nextq+t0] + psubw m3, m0 + psubw m4, m1 + PABS m3, m5 + PABS m4, m5 + paddw m3, m4 + psrlw m3, 1 + pmaxsw m2, m3 + mova [rsp+48], m2 + + paddw m1, m0 + paddw m0, m0 + psubw m0, m1 + psrlw m1, 1 + PABS m0, m2 + + movu m2, [curq+t1-1*2] + movu m3, [curq+t0-1*2] + mova m4, m2 + psubusw m2, m3 + psubusw m3, m4 + PMAXUW m2, m3 +%if mmsize == 16 + mova m3, m2 + psrldq m3, 4 +%else + mova m3, m2 + psrlq m3, 32 +%endif + paddw m0, m2 + paddw m0, m3 + psubw m0, [pw_1] + + CHECK -2, 0 + CHECK1 + CHECK -3, 1 + CHECK2 + CHECK 0, -2 + CHECK1 + CHECK 1, -3 + CHECK2 + + mova m6, [rsp+48] + cmp DWORD r8m, 2 + jge .end%1 + LOAD m2, [%2+t1*2] + LOAD m4, [%3+t1*2] + LOAD m3, [%2+t0*2] + LOAD m5, [%3+t0*2] + paddw m2, m4 + paddw m3, m5 + psrlw m2, 1 + psrlw m3, 1 + mova m4, [rsp+ 0] + mova m5, [rsp+16] + mova m7, [rsp+32] + psubw m2, m4 + psubw m3, m7 + mova m0, m5 + psubw m5, m4 + psubw m0, m7 + mova m4, m2 + pminsw m2, m3 + pmaxsw m3, m4 + pmaxsw m2, m5 + pminsw m3, m5 + pmaxsw m2, m0 + pminsw m3, m0 + pxor m4, m4 + pmaxsw m6, m3 + psubw m4, m2 + pmaxsw m6, m4 + +.end%1: + mova m2, [rsp+16] + mova m3, m2 + psubw m2, m6 + paddw m3, m6 + pmaxsw m1, m2 + pminsw m1, m3 + + movu [dstq], m1 + add dstq, mmsize-4 + add prevq, mmsize-4 + add curq, mmsize-4 + add nextq, mmsize-4 + sub DWORD r4m, mmsize/2-2 + jg .loop%1 +%endmacro + +%macro YADIF 0 +%if ARCH_X86_32 +cglobal yadif_filter_line_10bit, 4, 6, 8, 80, dst, prev, cur, next, w, \ + prefs, mrefs, parity, mode +%else +cglobal yadif_filter_line_10bit, 4, 7, 8, 80, dst, prev, cur, next, w, \ + prefs, mrefs, parity, mode +%endif +%if ARCH_X86_32 + mov r4, r5mp + mov r5, r6mp + DECLARE_REG_TMP 4,5 +%else + movsxd r5, DWORD r5m + movsxd r6, DWORD r6m + DECLARE_REG_TMP 5,6 +%endif + + cmp DWORD paritym, 0 + je .parity0 + FILTER 1, prevq, curq + jmp .ret + +.parity0: + FILTER 0, curq, nextq + +.ret: + RET +%endmacro + +INIT_XMM ssse3 +YADIF +INIT_XMM sse2 +YADIF +%if ARCH_X86_32 +INIT_MMX mmxext +YADIF +%endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/x86/yadif-16.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/x86/yadif-16.asm Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,347 @@ +;***************************************************************************** +;* x86-optimized functions for yadif filter +;* +;* Copyright (C) 2006 Michael Niedermayer +;* Copyright (c) 2013 Daniel Kang +;* Copyright (c) 2011-2013 James Darnley +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License along +;* with FFmpeg; if not, write to the Free Software Foundation, Inc., +;* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA + +pw_1: times 8 dw 1 +pw_8000: times 8 dw 0x8000 +pd_1: times 4 dd 1 +pd_8000: times 4 dd 0x8000 + +SECTION .text + +%macro PIXSHIFT1 1 +%if cpuflag(sse2) + psrldq %1, 2 +%else + psrlq %1, 16 +%endif +%endmacro + +%macro PIXSHIFT2 1 +%if cpuflag(sse2) + psrldq %1, 4 +%else + psrlq %1, 32 +%endif +%endmacro + +%macro PABS 2 +%if cpuflag(ssse3) + pabsd %1, %1 +%else + pxor %2, %2 + pcmpgtd %2, %1 + pxor %1, %2 + psubd %1, %2 +%endif +%endmacro + +%macro PACK 1 +%if cpuflag(sse4) + packusdw %1, %1 +%else + psubd %1, [pd_8000] + packssdw %1, %1 + paddw %1, [pw_8000] +%endif +%endmacro + +%macro PMINSD 3 +%if cpuflag(sse4) + pminsd %1, %2 +%else + mova %3, %2 + pcmpgtd %3, %1 + pand %1, %3 + pandn %3, %2 + por %1, %3 +%endif +%endmacro + +%macro PMAXSD 3 +%if cpuflag(sse4) + pmaxsd %1, %2 +%else + mova %3, %1 + pcmpgtd %3, %2 + pand %1, %3 + pandn %3, %2 + por %1, %3 +%endif +%endmacro + +%macro PMAXUW 2 +%if cpuflag(sse4) + pmaxuw %1, %2 +%else + psubusw %1, %2 + paddusw %1, %2 +%endif +%endmacro + +%macro CHECK 2 + movu m2, [curq+t1+%1*2] + movu m3, [curq+t0+%2*2] + mova m4, m2 + mova m5, m2 + pxor m4, m3 + pavgw m5, m3 + pand m4, [pw_1] + psubusw m5, m4 +%if mmsize == 16 + psrldq m5, 2 +%else + psrlq m5, 16 +%endif + punpcklwd m5, m7 + mova m4, m2 + psubusw m2, m3 + psubusw m3, m4 + PMAXUW m2, m3 + mova m3, m2 + mova m4, m2 +%if mmsize == 16 + psrldq m3, 2 + psrldq m4, 4 +%else + psrlq m3, 16 + psrlq m4, 32 +%endif + punpcklwd m2, m7 + punpcklwd m3, m7 + punpcklwd m4, m7 + paddd m2, m3 + paddd m2, m4 +%endmacro + +%macro CHECK1 0 + mova m3, m0 + pcmpgtd m3, m2 + PMINSD m0, m2, m6 + mova m6, m3 + pand m5, m3 + pandn m3, m1 + por m3, m5 + mova m1, m3 +%endmacro + +%macro CHECK2 0 + paddd m6, [pd_1] + pslld m6, 30 + paddd m2, m6 + mova m3, m0 + pcmpgtd m3, m2 + PMINSD m0, m2, m4 + pand m5, m3 + pandn m3, m1 + por m3, m5 + mova m1, m3 +%endmacro + +; This version of CHECK2 has 3 fewer instructions on sets older than SSE4 but I +; am not sure whether it is any faster. A rewrite or refactor of the filter +; code should make it possible to eliminate the move intruction at the end. It +; exists to satisfy the expectation that the "score" values are in m1. + +; %macro CHECK2 0 +; mova m3, m0 +; pcmpgtd m0, m2 +; pand m0, m6 +; mova m6, m0 +; pand m5, m6 +; pand m2, m0 +; pandn m6, m1 +; pandn m0, m3 +; por m6, m5 +; por m0, m2 +; mova m1, m6 +; %endmacro + +%macro LOAD 2 + movh %1, %2 + punpcklwd %1, m7 +%endmacro + +%macro FILTER 3 +.loop%1: + pxor m7, m7 + LOAD m0, [curq+t1] + LOAD m1, [curq+t0] + LOAD m2, [%2] + LOAD m3, [%3] + mova m4, m3 + paddd m3, m2 + psrad m3, 1 + mova [rsp+ 0], m0 + mova [rsp+16], m3 + mova [rsp+32], m1 + psubd m2, m4 + PABS m2, m4 + LOAD m3, [prevq+t1] + LOAD m4, [prevq+t0] + psubd m3, m0 + psubd m4, m1 + PABS m3, m5 + PABS m4, m5 + paddd m3, m4 + psrld m2, 1 + psrld m3, 1 + PMAXSD m2, m3, m6 + LOAD m3, [nextq+t1] + LOAD m4, [nextq+t0] + psubd m3, m0 + psubd m4, m1 + PABS m3, m5 + PABS m4, m5 + paddd m3, m4 + psrld m3, 1 + PMAXSD m2, m3, m6 + mova [rsp+48], m2 + + paddd m1, m0 + paddd m0, m0 + psubd m0, m1 + psrld m1, 1 + PABS m0, m2 + + movu m2, [curq+t1-1*2] + movu m3, [curq+t0-1*2] + mova m4, m2 + psubusw m2, m3 + psubusw m3, m4 + PMAXUW m2, m3 +%if mmsize == 16 + mova m3, m2 + psrldq m3, 4 +%else + mova m3, m2 + psrlq m3, 32 +%endif + punpcklwd m2, m7 + punpcklwd m3, m7 + paddd m0, m2 + paddd m0, m3 + psubd m0, [pd_1] + + CHECK -2, 0 + CHECK1 + CHECK -3, 1 + CHECK2 + CHECK 0, -2 + CHECK1 + CHECK 1, -3 + CHECK2 + + mova m6, [rsp+48] + cmp DWORD r8m, 2 + jge .end%1 + LOAD m2, [%2+t1*2] + LOAD m4, [%3+t1*2] + LOAD m3, [%2+t0*2] + LOAD m5, [%3+t0*2] + paddd m2, m4 + paddd m3, m5 + psrld m2, 1 + psrld m3, 1 + mova m4, [rsp+ 0] + mova m5, [rsp+16] + mova m7, [rsp+32] + psubd m2, m4 + psubd m3, m7 + mova m0, m5 + psubd m5, m4 + psubd m0, m7 + mova m4, m2 + PMINSD m2, m3, m7 + PMAXSD m3, m4, m7 + PMAXSD m2, m5, m7 + PMINSD m3, m5, m7 + PMAXSD m2, m0, m7 + PMINSD m3, m0, m7 + pxor m4, m4 + PMAXSD m6, m3, m7 + psubd m4, m2 + PMAXSD m6, m4, m7 + +.end%1: + mova m2, [rsp+16] + mova m3, m2 + psubd m2, m6 + paddd m3, m6 + PMAXSD m1, m2, m7 + PMINSD m1, m3, m7 + PACK m1 + + movh [dstq], m1 + add dstq, mmsize/2 + add prevq, mmsize/2 + add curq, mmsize/2 + add nextq, mmsize/2 + sub DWORD r4m, mmsize/4 + jg .loop%1 +%endmacro + +%macro YADIF 0 +%if ARCH_X86_32 +cglobal yadif_filter_line_16bit, 4, 6, 8, 80, dst, prev, cur, next, w, \ + prefs, mrefs, parity, mode +%else +cglobal yadif_filter_line_16bit, 4, 7, 8, 80, dst, prev, cur, next, w, \ + prefs, mrefs, parity, mode +%endif +%if ARCH_X86_32 + mov r4, r5mp + mov r5, r6mp + DECLARE_REG_TMP 4,5 +%else + movsxd r5, DWORD r5m + movsxd r6, DWORD r6m + DECLARE_REG_TMP 5,6 +%endif + + cmp DWORD paritym, 0 + je .parity0 + FILTER 1, prevq, curq + jmp .ret + +.parity0: + FILTER 0, curq, nextq + +.ret: + RET +%endmacro + +INIT_XMM sse4 +YADIF +INIT_XMM ssse3 +YADIF +INIT_XMM sse2 +YADIF +%if ARCH_X86_32 +INIT_MMX mmxext +YADIF +%endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavfilter/yadif.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavfilter/yadif.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,74 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AVFILTER_YADIF_H +#define AVFILTER_YADIF_H + +#include "libavutil/pixdesc.h" +#include "avfilter.h" + +enum YADIFMode { + YADIF_MODE_SEND_FRAME = 0, ///< send 1 frame for each frame + YADIF_MODE_SEND_FIELD = 1, ///< send 1 frame for each field + YADIF_MODE_SEND_FRAME_NOSPATIAL = 2, ///< send 1 frame for each frame but skips spatial interlacing check + YADIF_MODE_SEND_FIELD_NOSPATIAL = 3, ///< send 1 frame for each field but skips spatial interlacing check +}; + +enum YADIFParity { + YADIF_PARITY_TFF = 0, ///< top field first + YADIF_PARITY_BFF = 1, ///< bottom field first + YADIF_PARITY_AUTO = -1, ///< auto detection +}; + +enum YADIFDeint { + YADIF_DEINT_ALL = 0, ///< deinterlace all frames + YADIF_DEINT_INTERLACED = 1, ///< only deinterlace frames marked as interlaced +}; + +typedef struct YADIFContext { + const AVClass *class; + + enum YADIFMode mode; + enum YADIFParity parity; + enum YADIFDeint deint; + + int frame_pending; + + AVFrame *cur; + AVFrame *next; + AVFrame *prev; + AVFrame *out; + + /** + * Required alignment for filter_line + */ + void (*filter_line)(void *dst, + void *prev, void *cur, void *next, + int w, int prefs, int mrefs, int parity, int mode); + void (*filter_edges)(void *dst, void *prev, void *cur, void *next, + int w, int prefs, int mrefs, int parity, int mode); + + const AVPixFmtDescriptor *csp; + int eof; + uint8_t *temp_line; + int temp_line_size; +} YADIFContext; + +void ff_yadif_init_x86(YADIFContext *yadif); + +#endif /* AVFILTER_YADIF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/4xm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/4xm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,372 @@ +/* + * 4X Technologies .4xm File Demuxer (no muxer) + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * 4X Technologies file demuxer + * by Mike Melanson (melanson@pcisys.net) + * for more information on the .4xm file format, visit: + * http://www.pcisys.net/~melanson/codecs/ + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "internal.h" + +#define RIFF_TAG MKTAG('R', 'I', 'F', 'F') +#define FOURXMV_TAG MKTAG('4', 'X', 'M', 'V') +#define LIST_TAG MKTAG('L', 'I', 'S', 'T') +#define HEAD_TAG MKTAG('H', 'E', 'A', 'D') +#define TRK__TAG MKTAG('T', 'R', 'K', '_') +#define MOVI_TAG MKTAG('M', 'O', 'V', 'I') +#define VTRK_TAG MKTAG('V', 'T', 'R', 'K') +#define STRK_TAG MKTAG('S', 'T', 'R', 'K') +#define std__TAG MKTAG('s', 't', 'd', '_') +#define name_TAG MKTAG('n', 'a', 'm', 'e') +#define vtrk_TAG MKTAG('v', 't', 'r', 'k') +#define strk_TAG MKTAG('s', 't', 'r', 'k') +#define ifrm_TAG MKTAG('i', 'f', 'r', 'm') +#define pfrm_TAG MKTAG('p', 'f', 'r', 'm') +#define cfrm_TAG MKTAG('c', 'f', 'r', 'm') +#define ifr2_TAG MKTAG('i', 'f', 'r', '2') +#define pfr2_TAG MKTAG('p', 'f', 'r', '2') +#define cfr2_TAG MKTAG('c', 'f', 'r', '2') +#define snd__TAG MKTAG('s', 'n', 'd', '_') + +#define vtrk_SIZE 0x44 +#define strk_SIZE 0x28 + +#define GET_LIST_HEADER() \ + fourcc_tag = avio_rl32(pb); \ + size = avio_rl32(pb); \ + if (fourcc_tag != LIST_TAG) \ + return AVERROR_INVALIDDATA; \ + fourcc_tag = avio_rl32(pb); + +typedef struct AudioTrack { + int sample_rate; + int bits; + int channels; + int stream_index; + int adpcm; + int64_t audio_pts; +} AudioTrack; + +typedef struct FourxmDemuxContext { + int width; + int height; + int video_stream_index; + int track_count; + AudioTrack *tracks; + + int64_t video_pts; + float fps; +} FourxmDemuxContext; + +static int fourxm_probe(AVProbeData *p) +{ + if ((AV_RL32(&p->buf[0]) != RIFF_TAG) || + (AV_RL32(&p->buf[8]) != FOURXMV_TAG)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int fourxm_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + unsigned int fourcc_tag; + unsigned int size; + int header_size; + FourxmDemuxContext *fourxm = s->priv_data; + unsigned char *header; + int i, ret; + AVStream *st; + + fourxm->track_count = 0; + fourxm->tracks = NULL; + fourxm->fps = 1.0; + + /* skip the first 3 32-bit numbers */ + avio_skip(pb, 12); + + /* check for LIST-HEAD */ + GET_LIST_HEADER(); + header_size = size - 4; + if (fourcc_tag != HEAD_TAG || header_size < 0) + return AVERROR_INVALIDDATA; + + /* allocate space for the header and load the whole thing */ + header = av_malloc(header_size); + if (!header) + return AVERROR(ENOMEM); + if (avio_read(pb, header, header_size) != header_size){ + av_free(header); + return AVERROR(EIO); + } + + /* take the lazy approach and search for any and all vtrk and strk chunks */ + for (i = 0; i < header_size - 8; i++) { + fourcc_tag = AV_RL32(&header[i]); + size = AV_RL32(&header[i + 4]); + if (size > header_size - i - 8 && (fourcc_tag == vtrk_TAG || fourcc_tag == strk_TAG)) { + av_log(s, AV_LOG_ERROR, "chunk larger than array %d>%d\n", size, header_size - i - 8); + return AVERROR_INVALIDDATA; + } + + if (fourcc_tag == std__TAG) { + if (header_size < i + 16) { + av_log(s, AV_LOG_ERROR, "std TAG truncated\n"); + return AVERROR_INVALIDDATA; + } + fourxm->fps = av_int2float(AV_RL32(&header[i + 12])); + } else if (fourcc_tag == vtrk_TAG) { + /* check that there is enough data */ + if (size != vtrk_SIZE) { + ret= AVERROR_INVALIDDATA; + goto fail; + } + fourxm->width = AV_RL32(&header[i + 36]); + fourxm->height = AV_RL32(&header[i + 40]); + + /* allocate a new AVStream */ + st = avformat_new_stream(s, NULL); + if (!st){ + ret= AVERROR(ENOMEM); + goto fail; + } + avpriv_set_pts_info(st, 60, 1, fourxm->fps); + + fourxm->video_stream_index = st->index; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_4XM; + st->codec->extradata_size = 4; + st->codec->extradata = av_malloc(4); + AV_WL32(st->codec->extradata, AV_RL32(&header[i + 16])); + st->codec->width = fourxm->width; + st->codec->height = fourxm->height; + + i += 8 + size; + } else if (fourcc_tag == strk_TAG) { + int current_track; + /* check that there is enough data */ + if (size != strk_SIZE) { + ret= AVERROR_INVALIDDATA; + goto fail; + } + current_track = AV_RL32(&header[i + 8]); + if((unsigned)current_track >= UINT_MAX / sizeof(AudioTrack) - 1){ + av_log(s, AV_LOG_ERROR, "current_track too large\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (current_track + 1 > fourxm->track_count) { + fourxm->tracks = av_realloc_f(fourxm->tracks, + sizeof(AudioTrack), + current_track + 1); + if (!fourxm->tracks) { + ret = AVERROR(ENOMEM); + goto fail; + } + memset(&fourxm->tracks[fourxm->track_count], 0, + sizeof(AudioTrack) * (current_track + 1 - fourxm->track_count)); + fourxm->track_count = current_track + 1; + } + fourxm->tracks[current_track].adpcm = AV_RL32(&header[i + 12]); + fourxm->tracks[current_track].channels = AV_RL32(&header[i + 36]); + fourxm->tracks[current_track].sample_rate = AV_RL32(&header[i + 40]); + fourxm->tracks[current_track].bits = AV_RL32(&header[i + 44]); + fourxm->tracks[current_track].audio_pts = 0; + if( fourxm->tracks[current_track].channels <= 0 + || fourxm->tracks[current_track].sample_rate <= 0 + || fourxm->tracks[current_track].bits < 0){ + av_log(s, AV_LOG_ERROR, "audio header invalid\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } + if(!fourxm->tracks[current_track].adpcm && fourxm->tracks[current_track].bits<8){ + av_log(s, AV_LOG_ERROR, "bits unspecified for non ADPCM\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } + i += 8 + size; + + /* allocate a new AVStream */ + st = avformat_new_stream(s, NULL); + if (!st){ + ret= AVERROR(ENOMEM); + goto fail; + } + + st->id = current_track; + avpriv_set_pts_info(st, 60, 1, fourxm->tracks[current_track].sample_rate); + + fourxm->tracks[current_track].stream_index = st->index; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 0; + st->codec->channels = fourxm->tracks[current_track].channels; + st->codec->sample_rate = fourxm->tracks[current_track].sample_rate; + st->codec->bits_per_coded_sample = fourxm->tracks[current_track].bits; + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * + st->codec->bits_per_coded_sample; + st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; + if (fourxm->tracks[current_track].adpcm){ + st->codec->codec_id = AV_CODEC_ID_ADPCM_4XM; + }else if (st->codec->bits_per_coded_sample == 8){ + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + }else + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + } + } + + /* skip over the LIST-MOVI chunk (which is where the stream should be */ + GET_LIST_HEADER(); + if (fourcc_tag != MOVI_TAG){ + ret= AVERROR_INVALIDDATA; + goto fail; + } + + av_free(header); + /* initialize context members */ + fourxm->video_pts = -1; /* first frame will push to 0 */ + + return 0; +fail: + av_freep(&fourxm->tracks); + av_free(header); + return ret; +} + +static int fourxm_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + FourxmDemuxContext *fourxm = s->priv_data; + AVIOContext *pb = s->pb; + unsigned int fourcc_tag; + unsigned int size; + int ret = 0; + unsigned int track_number; + int packet_read = 0; + unsigned char header[8]; + int audio_frame_count; + + while (!packet_read) { + + if ((ret = avio_read(s->pb, header, 8)) < 0) + return ret; + fourcc_tag = AV_RL32(&header[0]); + size = AV_RL32(&header[4]); + if (url_feof(pb)) + return AVERROR(EIO); + switch (fourcc_tag) { + + case LIST_TAG: + /* this is a good time to bump the video pts */ + fourxm->video_pts ++; + + /* skip the LIST-* tag and move on to the next fourcc */ + avio_rl32(pb); + break; + + case ifrm_TAG: + case pfrm_TAG: + case cfrm_TAG: + case ifr2_TAG: + case pfr2_TAG: + case cfr2_TAG: + /* allocate 8 more bytes than 'size' to account for fourcc + * and size */ + if (size + 8 < size || av_new_packet(pkt, size + 8)) + return AVERROR(EIO); + pkt->stream_index = fourxm->video_stream_index; + pkt->pts = fourxm->video_pts; + pkt->pos = avio_tell(s->pb); + memcpy(pkt->data, header, 8); + ret = avio_read(s->pb, &pkt->data[8], size); + + if (ret < 0){ + av_free_packet(pkt); + }else + packet_read = 1; + break; + + case snd__TAG: + track_number = avio_rl32(pb); + avio_skip(pb, 4); + size-=8; + + if (track_number < fourxm->track_count && fourxm->tracks[track_number].channels>0) { + ret= av_get_packet(s->pb, pkt, size); + if(ret<0) + return AVERROR(EIO); + pkt->stream_index = + fourxm->tracks[track_number].stream_index; + pkt->pts = fourxm->tracks[track_number].audio_pts; + packet_read = 1; + + /* pts accounting */ + audio_frame_count = size; + if (fourxm->tracks[track_number].adpcm) + audio_frame_count -= + 2 * (fourxm->tracks[track_number].channels); + audio_frame_count /= + fourxm->tracks[track_number].channels; + if (fourxm->tracks[track_number].adpcm){ + audio_frame_count *= 2; + }else + audio_frame_count /= + (fourxm->tracks[track_number].bits / 8); + fourxm->tracks[track_number].audio_pts += audio_frame_count; + + } else { + avio_skip(pb, size); + } + break; + + default: + avio_skip(pb, size); + break; + } + } + return ret; +} + +static int fourxm_read_close(AVFormatContext *s) +{ + FourxmDemuxContext *fourxm = s->priv_data; + + av_freep(&fourxm->tracks); + + return 0; +} + +AVInputFormat ff_fourxm_demuxer = { + .name = "4xm", + .long_name = NULL_IF_CONFIG_SMALL("4X Technologies"), + .priv_data_size = sizeof(FourxmDemuxContext), + .read_probe = fourxm_probe, + .read_header = fourxm_read_header, + .read_packet = fourxm_read_packet, + .read_close = fourxm_read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/4xm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/4xm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/4xm.o libavformat/4xm.o: libavformat/4xm.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/4xm.o Binary file ffmpeg/libavformat/4xm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/Makefile Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,454 @@ +include $(SUBDIR)../config.mak + +NAME = avformat +FFLIBS = avcodec avutil + +HEADERS = avformat.h \ + avio.h \ + version.h \ + +OBJS = allformats.o \ + avio.o \ + aviobuf.o \ + cutils.o \ + id3v1.o \ + id3v2.o \ + metadata.o \ + mux.o \ + options.o \ + os_support.o \ + riff.o \ + sdp.o \ + seek.o \ + utils.o \ + +OBJS-$(CONFIG_NETWORK) += network.o +OBJS-$(CONFIG_RTPDEC) += rdt.o \ + rtp.o \ + rtpdec.o \ + rtpdec_amr.o \ + rtpdec_asf.o \ + rtpdec_g726.o \ + rtpdec_h263.o \ + rtpdec_h263_rfc2190.o \ + rtpdec_h264.o \ + rtpdec_ilbc.o \ + rtpdec_jpeg.o \ + rtpdec_latm.o \ + rtpdec_mpeg12.o \ + rtpdec_mpeg4.o \ + rtpdec_mpegts.o \ + rtpdec_qcelp.o \ + rtpdec_qdm2.o \ + rtpdec_qt.o \ + rtpdec_svq3.o \ + rtpdec_vp8.o \ + rtpdec_xiph.o \ + srtp.o +OBJS-$(CONFIG_RTPENC_CHAIN) += rtpenc_chain.o rtp.o +OBJS-$(CONFIG_SHARED) += log2_tab.o + +# muxers/demuxers +OBJS-$(CONFIG_A64_MUXER) += a64.o rawenc.o +OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o rawdec.o +OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o +OBJS-$(CONFIG_AC3_MUXER) += rawenc.o +OBJS-$(CONFIG_ACT_DEMUXER) += act.o +OBJS-$(CONFIG_ADF_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o +OBJS-$(CONFIG_ADX_MUXER) += rawenc.o +OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o +OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o +OBJS-$(CONFIG_AFC_DEMUXER) += afc.o +OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o isom.o \ + mov_chan.o +OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o isom.o id3v2enc.o +OBJS-$(CONFIG_AMR_DEMUXER) += amr.o +OBJS-$(CONFIG_AMR_MUXER) += amr.o +OBJS-$(CONFIG_ANM_DEMUXER) += anm.o +OBJS-$(CONFIG_APC_DEMUXER) += apc.o +OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o img2.o +OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o +OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o \ + avlanguage.o +OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o +OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o subtitles.o +OBJS-$(CONFIG_ASS_MUXER) += assenc.o +OBJS-$(CONFIG_AST_DEMUXER) += ast.o astdec.o +OBJS-$(CONFIG_AST_MUXER) += ast.o astenc.o +OBJS-$(CONFIG_AU_DEMUXER) += au.o pcm.o +OBJS-$(CONFIG_AU_MUXER) += au.o rawenc.o +OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o +OBJS-$(CONFIG_AVI_MUXER) += avienc.o +OBJS-$(CONFIG_AVISYNTH) += avisynth.o +OBJS-$(CONFIG_AVM2_MUXER) += swfenc.o swf.o +OBJS-$(CONFIG_AVR_DEMUXER) += avr.o pcm.o +OBJS-$(CONFIG_AVS_DEMUXER) += avs.o vocdec.o voc.o +OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o +OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o +OBJS-$(CONFIG_BINK_DEMUXER) += bink.o +OBJS-$(CONFIG_BINTEXT_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_BIT_DEMUXER) += bit.o +OBJS-$(CONFIG_BIT_MUXER) += bit.o +OBJS-$(CONFIG_BMV_DEMUXER) += bmv.o +OBJS-$(CONFIG_BRSTM_DEMUXER) += brstm.o +OBJS-$(CONFIG_C93_DEMUXER) += c93.o vocdec.o voc.o +OBJS-$(CONFIG_CAF_DEMUXER) += cafdec.o caf.o mov.o mov_chan.o \ + isom.o +OBJS-$(CONFIG_CAF_MUXER) += cafenc.o caf.o riff.o isom.o +OBJS-$(CONFIG_CAVSVIDEO_DEMUXER) += cavsvideodec.o rawdec.o +OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o +OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o +OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o +OBJS-$(CONFIG_CRC_MUXER) += crcenc.o +OBJS-$(CONFIG_DAUD_DEMUXER) += daud.o +OBJS-$(CONFIG_DAUD_MUXER) += daud.o +OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o +OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o +OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o +OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o +OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o +OBJS-$(CONFIG_DSICIN_DEMUXER) += dsicin.o +OBJS-$(CONFIG_DTSHD_DEMUXER) += dtshddec.o +OBJS-$(CONFIG_DTS_DEMUXER) += dtsdec.o rawdec.o +OBJS-$(CONFIG_DTS_MUXER) += rawenc.o +OBJS-$(CONFIG_DV_DEMUXER) += dv.o +OBJS-$(CONFIG_DV_MUXER) += dvenc.o +OBJS-$(CONFIG_DXA_DEMUXER) += dxa.o +OBJS-$(CONFIG_EA_CDATA_DEMUXER) += eacdata.o +OBJS-$(CONFIG_EA_DEMUXER) += electronicarts.o +OBJS-$(CONFIG_EAC3_DEMUXER) += ac3dec.o rawdec.o +OBJS-$(CONFIG_EAC3_MUXER) += rawenc.o +OBJS-$(CONFIG_EPAF_DEMUXER) += epafdec.o pcm.o +OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o +OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o +OBJS-$(CONFIG_FFMETADATA_DEMUXER) += ffmetadec.o +OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o +OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o +OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o +OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \ + oggparsevorbis.o \ + vorbiscomment.o +OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ + vorbiscomment.o +OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o +OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o +OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o +OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o +OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o +OBJS-$(CONFIG_FRAMEMD5_MUXER) += md5enc.o framehash.o +OBJS-$(CONFIG_FRM_DEMUXER) += frmdec.o +OBJS-$(CONFIG_GIF_MUXER) += gif.o +OBJS-$(CONFIG_GIF_DEMUXER) += gifdec.o +OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o +OBJS-$(CONFIG_GXF_DEMUXER) += gxf.o +OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o audiointerleave.o +OBJS-$(CONFIG_G722_DEMUXER) += g722.o rawdec.o +OBJS-$(CONFIG_G722_MUXER) += rawenc.o +OBJS-$(CONFIG_G723_1_DEMUXER) += g723_1.o +OBJS-$(CONFIG_G723_1_MUXER) += rawenc.o +OBJS-$(CONFIG_G729_DEMUXER) += g729dec.o +OBJS-$(CONFIG_H261_DEMUXER) += h261dec.o rawdec.o +OBJS-$(CONFIG_H261_MUXER) += rawenc.o +OBJS-$(CONFIG_H263_DEMUXER) += h263dec.o rawdec.o +OBJS-$(CONFIG_H263_MUXER) += rawenc.o +OBJS-$(CONFIG_H264_DEMUXER) += h264dec.o rawdec.o +OBJS-$(CONFIG_H264_MUXER) += rawenc.o +OBJS-$(CONFIG_HLS_DEMUXER) += hls.o +OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o mpegtsenc.o +OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o +OBJS-$(CONFIG_ICO_MUXER) += icoenc.o +OBJS-$(CONFIG_IDCIN_DEMUXER) += idcin.o +OBJS-$(CONFIG_IDF_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_IFF_DEMUXER) += iff.o +OBJS-$(CONFIG_ILBC_DEMUXER) += ilbc.o +OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o +OBJS-$(CONFIG_IMAGE2_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE2_MUXER) += img2enc.o img2.o +OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER) += img2dec.o img2.o +OBJS-$(CONFIG_IMAGE2PIPE_MUXER) += img2enc.o img2.o +OBJS-$(CONFIG_INGENIENT_DEMUXER) += ingenientdec.o rawdec.o +OBJS-$(CONFIG_IPMOVIE_DEMUXER) += ipmovie.o +OBJS-$(CONFIG_IRCAM_DEMUXER) += ircamdec.o ircam.o pcm.o +OBJS-$(CONFIG_IRCAM_MUXER) += ircamenc.o ircam.o rawenc.o +OBJS-$(CONFIG_ISS_DEMUXER) += iss.o +OBJS-$(CONFIG_IV8_DEMUXER) += iv8.o +OBJS-$(CONFIG_IVF_DEMUXER) += ivfdec.o +OBJS-$(CONFIG_IVF_MUXER) += ivfenc.o +OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o +OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o +OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o +OBJS-$(CONFIG_LATM_DEMUXER) += rawdec.o +OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o +OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o +OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o +OBJS-$(CONFIG_LVF_DEMUXER) += lvfdec.o +OBJS-$(CONFIG_LXF_DEMUXER) += lxfdec.o +OBJS-$(CONFIG_M4V_DEMUXER) += m4vdec.o rawdec.o +OBJS-$(CONFIG_M4V_MUXER) += rawenc.o +OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \ + isom.o rmsipr.o +OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ + isom.o avc.o \ + flacenc_header.o avlanguage.o +OBJS-$(CONFIG_MD5_MUXER) += md5enc.o +OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o +OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o subtitles.o +OBJS-$(CONFIG_MICRODVD_MUXER) += microdvdenc.o +OBJS-$(CONFIG_MJPEG_DEMUXER) += rawdec.o +OBJS-$(CONFIG_MJPEG_MUXER) += rawenc.o +OBJS-$(CONFIG_MLP_DEMUXER) += rawdec.o +OBJS-$(CONFIG_MLP_MUXER) += rawenc.o +OBJS-$(CONFIG_MM_DEMUXER) += mm.o +OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o +OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o +OBJS-$(CONFIG_MOV_DEMUXER) += mov.o isom.o mov_chan.o +OBJS-$(CONFIG_MOV_MUXER) += movenc.o isom.o avc.o \ + movenchint.o mov_chan.o rtp.o +OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o +OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o +OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o +OBJS-$(CONFIG_MPC_DEMUXER) += mpc.o apetag.o img2.o +OBJS-$(CONFIG_MPC8_DEMUXER) += mpc8.o apetag.o img2.o +OBJS-$(CONFIG_MPEG1SYSTEM_MUXER) += mpegenc.o +OBJS-$(CONFIG_MPEG1VCD_MUXER) += mpegenc.o +OBJS-$(CONFIG_MPEG2DVD_MUXER) += mpegenc.o +OBJS-$(CONFIG_MPEG2VOB_MUXER) += mpegenc.o +OBJS-$(CONFIG_MPEG2SVCD_MUXER) += mpegenc.o +OBJS-$(CONFIG_MPEG1VIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_MPEG2VIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_MPEGPS_DEMUXER) += mpeg.o +OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o +OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o +OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o +OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o +OBJS-$(CONFIG_MPL2_DEMUXER) += mpl2dec.o subtitles.o +OBJS-$(CONFIG_MPSUB_DEMUXER) += mpsubdec.o subtitles.o +OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o +OBJS-$(CONFIG_MTV_DEMUXER) += mtv.o +OBJS-$(CONFIG_MVI_DEMUXER) += mvi.o +OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o +OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o +OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o audiointerleave.o +OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o +OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o +OBJS-$(CONFIG_NISTSPHERE_DEMUXER) += nistspheredec.o pcm.o +OBJS-$(CONFIG_NSV_DEMUXER) += nsvdec.o +OBJS-$(CONFIG_NULL_MUXER) += nullenc.o +OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o +OBJS-$(CONFIG_NUT_MUXER) += nutenc.o nut.o +OBJS-$(CONFIG_NUV_DEMUXER) += nuv.o +OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ + oggparsecelt.o \ + oggparsedirac.o \ + oggparseflac.o \ + oggparseogm.o \ + oggparseopus.o \ + oggparseskeleton.o \ + oggparsespeex.o \ + oggparsetheora.o \ + oggparsevorbis.o \ + vorbiscomment.o +OBJS-$(CONFIG_OGG_MUXER) += oggenc.o \ + vorbiscomment.o +OBJS-$(CONFIG_OMA_DEMUXER) += omadec.o pcm.o oma.o +OBJS-$(CONFIG_OMA_MUXER) += omaenc.o rawenc.o oma.o id3v2enc.o +OBJS-$(CONFIG_PAF_DEMUXER) += paf.o +OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_ALAW_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_F32BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_F32BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_F32LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_F32LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_F64BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_F64BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_F64LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_F64LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_MULAW_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_MULAW_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S16BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S16BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S16LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S16LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S24BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S24BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S24LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S24LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S32BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S32BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S32LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S32LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_S8_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_S8_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U16BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U16BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U16LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U16LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U24BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U24BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U24LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U24LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U32BE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U32BE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U32LE_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U32LE_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o +OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PJS_DEMUXER) += pjsdec.o subtitles.o +OBJS-$(CONFIG_PMP_DEMUXER) += pmpdec.o +OBJS-$(CONFIG_PVA_DEMUXER) += pva.o +OBJS-$(CONFIG_PVF_DEMUXER) += pvfdec.o pcm.o +OBJS-$(CONFIG_QCP_DEMUXER) += qcp.o +OBJS-$(CONFIG_R3D_DEMUXER) += r3d.o +OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o +OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o +OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o +OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o +OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o +OBJS-$(CONFIG_ROQ_DEMUXER) += idroqdec.o +OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o +OBJS-$(CONFIG_RSO_DEMUXER) += rsodec.o rso.o pcm.o +OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o +OBJS-$(CONFIG_RPL_DEMUXER) += rpl.o +OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ + rtpenc_aac.o \ + rtpenc_latm.o \ + rtpenc_amr.o \ + rtpenc_h263.o \ + rtpenc_h263_rfc2190.o \ + rtpenc_jpeg.o \ + rtpenc_mpv.o \ + rtpenc.o \ + rtpenc_h264.o \ + rtpenc_vp8.o \ + rtpenc_xiph.o \ + avc.o +OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o \ + urldecode.o +OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \ + urldecode.o +OBJS-$(CONFIG_SAMI_DEMUXER) += samidec.o subtitles.o +OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o +OBJS-$(CONFIG_SAP_MUXER) += sapenc.o +OBJS-$(CONFIG_SBG_DEMUXER) += sbgdec.o +OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o +OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o +OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o +OBJS-$(CONFIG_SHORTEN_DEMUXER) += rawdec.o +OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o +OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o +OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o +OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o +OBJS-$(CONFIG_SMOOTHSTREAMING_MUXER) += smoothstreamingenc.o isom.o +OBJS-$(CONFIG_SMUSH_DEMUXER) += smush.o +OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o +OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o +OBJS-$(CONFIG_SOX_MUXER) += soxenc.o rawenc.o +OBJS-$(CONFIG_SPDIF_DEMUXER) += spdif.o spdifdec.o +OBJS-$(CONFIG_SPDIF_MUXER) += spdif.o spdifenc.o +OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o subtitles.o +OBJS-$(CONFIG_SRT_MUXER) += srtenc.o +OBJS-$(CONFIG_STR_DEMUXER) += psxstr.o +OBJS-$(CONFIG_SUBVIEWER1_DEMUXER) += subviewer1dec.o subtitles.o +OBJS-$(CONFIG_SUBVIEWER_DEMUXER) += subviewerdec.o subtitles.o +OBJS-$(CONFIG_SWF_DEMUXER) += swfdec.o swf.o +OBJS-$(CONFIG_SWF_MUXER) += swfenc.o swf.o +OBJS-$(CONFIG_TAK_DEMUXER) += takdec.o apetag.o img2.o rawdec.o +OBJS-$(CONFIG_TEDCAPTIONS_DEMUXER) += tedcaptionsdec.o subtitles.o +OBJS-$(CONFIG_TEE_MUXER) += tee.o +OBJS-$(CONFIG_THP_DEMUXER) += thp.o +OBJS-$(CONFIG_TIERTEXSEQ_DEMUXER) += tiertexseq.o +OBJS-$(CONFIG_MKVTIMESTAMP_V2_MUXER) += mkvtimestamp_v2.o +OBJS-$(CONFIG_TMV_DEMUXER) += tmv.o +OBJS-$(CONFIG_TRUEHD_DEMUXER) += rawdec.o +OBJS-$(CONFIG_TRUEHD_MUXER) += rawenc.o +OBJS-$(CONFIG_TTA_DEMUXER) += tta.o +OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o +OBJS-$(CONFIG_TXD_DEMUXER) += txd.o +OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o +OBJS-$(CONFIG_VC1T_DEMUXER) += vc1test.o +OBJS-$(CONFIG_VC1T_MUXER) += vc1testenc.o +OBJS-$(CONFIG_VIVO_DEMUXER) += vivo.o +OBJS-$(CONFIG_VMD_DEMUXER) += sierravmd.o +OBJS-$(CONFIG_VOBSUB_DEMUXER) += subtitles.o # mpeg demuxer is in the dependencies +OBJS-$(CONFIG_VOC_DEMUXER) += vocdec.o voc.o +OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o +OBJS-$(CONFIG_VPLAYER_DEMUXER) += vplayerdec.o subtitles.o +OBJS-$(CONFIG_VQF_DEMUXER) += vqf.o +OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o w64.o pcm.o +OBJS-$(CONFIG_W64_MUXER) += wavenc.o w64.o +OBJS-$(CONFIG_WAV_DEMUXER) += wavdec.o pcm.o +OBJS-$(CONFIG_WAV_MUXER) += wavenc.o +OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o +OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ + isom.o avc.o \ + flacenc_header.o avlanguage.o +OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o +OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o +OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o +OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv.o asfdec.o asf.o asfcrypt.o \ + avlanguage.o mpegts.o isom.o +OBJS-$(CONFIG_WTV_MUXER) += wtvenc.o wtv.o asf.o asfenc.o +OBJS-$(CONFIG_WV_DEMUXER) += wv.o apetag.o img2.o +OBJS-$(CONFIG_WV_MUXER) += wvenc.o apetagenc.o +OBJS-$(CONFIG_XA_DEMUXER) += xa.o +OBJS-$(CONFIG_XBIN_DEMUXER) += bintext.o sauce.o +OBJS-$(CONFIG_XMV_DEMUXER) += xmv.o +OBJS-$(CONFIG_XWMA_DEMUXER) += xwma.o +OBJS-$(CONFIG_YOP_DEMUXER) += yop.o +OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg.o +OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o + +# external libraries +OBJS-$(CONFIG_LIBMODPLUG_DEMUXER) += libmodplug.o +OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o +OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o +OBJS-$(CONFIG_LIBQUVI_DEMUXER) += libquvi.o +OBJS-$(CONFIG_LIBRTMP) += librtmp.o + +# protocols I/O +OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o +OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o +OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o +OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o +OBJS-$(CONFIG_CRYPTO_PROTOCOL) += crypto.o +OBJS-$(CONFIG_DATA_PROTOCOL) += data_uri.o +OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpcrypt.o rtmpdh.o +OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL) += rtmphttp.o +OBJS-$(CONFIG_FILE_PROTOCOL) += file.o +OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o +OBJS-$(CONFIG_HLS_PROTOCOL) += hlsproto.o +OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o urldecode.o +OBJS-$(CONFIG_HTTPPROXY_PROTOCOL) += http.o httpauth.o urldecode.o +OBJS-$(CONFIG_HTTPS_PROTOCOL) += http.o httpauth.o urldecode.o +OBJS-$(CONFIG_MMSH_PROTOCOL) += mmsh.o mms.o asf.o +OBJS-$(CONFIG_MMST_PROTOCOL) += mmst.o mms.o asf.o +OBJS-$(CONFIG_MD5_PROTOCOL) += md5proto.o +OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o +OBJS-$(CONFIG_RTMP_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMPE_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMPS_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMPT_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMPTE_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTMPTS_PROTOCOL) += rtmpproto.o rtmppkt.o +OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o +OBJS-$(CONFIG_SCTP_PROTOCOL) += sctp.o +OBJS-$(CONFIG_SRTP_PROTOCOL) += srtpproto.o srtp.o +OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o +OBJS-$(CONFIG_TLS_PROTOCOL) += tls.o +OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o + +SKIPHEADERS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpdh.h +SKIPHEADERS-$(CONFIG_NETWORK) += network.h rtsp.h +TESTPROGS = noproxy \ + seek \ + srtp \ + url \ + +TOOLS = aviocat \ + ismindex \ + pktdumper \ + probetest \ + seek_print \ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/a64.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/a64.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,62 @@ +/* + * a64 muxer + * Copyright (c) 2009 Tobias Bindhammer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/avcodec.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "rawenc.h" + +static int a64_write_header(struct AVFormatContext *s) +{ + AVCodecContext *avctx = s->streams[0]->codec; + uint8_t header[5] = { + 0x00, //load + 0x40, //address + 0x00, //mode + 0x00, //charset_lifetime (multi only) + 0x00 //fps in 50/fps; + }; + switch (avctx->codec->id) { + case AV_CODEC_ID_A64_MULTI: + header[2] = 0x00; + header[3] = AV_RB32(avctx->extradata+0); + header[4] = 2; + break; + case AV_CODEC_ID_A64_MULTI5: + header[2] = 0x01; + header[3] = AV_RB32(avctx->extradata+0); + header[4] = 3; + break; + default: + return AVERROR(EINVAL); + } + avio_write(s->pb, header, 2); + return 0; +} + +AVOutputFormat ff_a64_muxer = { + .name = "a64", + .long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"), + .extensions = "a64, A64", + .video_codec = AV_CODEC_ID_A64_MULTI, + .write_header = a64_write_header, + .write_packet = ff_raw_write_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/a64.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/a64.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/a64.o libavformat/a64.o: libavformat/a64.c \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavcodec/bytestream.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/a64.o Binary file ffmpeg/libavformat/a64.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aacdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aacdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,94 @@ +/* + * raw ADTS AAC demuxer + * Copyright (c) 2008 Michael Niedermayer + * Copyright (c) 2009 Robert Swain ( rob opendot cl ) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" +#include "id3v1.h" + + +static int adts_aac_probe(AVProbeData *p) +{ + int max_frames = 0, first_frames = 0; + int fsize, frames; + const uint8_t *buf0 = p->buf; + const uint8_t *buf2; + const uint8_t *buf; + const uint8_t *end = buf0 + p->buf_size - 7; + + buf = buf0; + + for(; buf < end; buf= buf2+1) { + buf2 = buf; + + for(frames = 0; buf2 < end; frames++) { + uint32_t header = AV_RB16(buf2); + if((header&0xFFF6) != 0xFFF0) + break; + fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF; + if(fsize < 7) + break; + fsize = FFMIN(fsize, end - buf2); + buf2 += fsize; + } + max_frames = FFMAX(max_frames, frames); + if(buf == buf0) + first_frames= frames; + } + if (first_frames>=3) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>500)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=3) return AVPROBE_SCORE_MAX/4; + else if(max_frames>=1) return 1; + else return 0; +} + +static int adts_aac_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + ff_id3v1_read(s); + + //LCM of all possible ADTS sample rates + avpriv_set_pts_info(st, 64, 1, 28224000); + + return 0; +} + +AVInputFormat ff_aac_demuxer = { + .name = "aac", + .long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), + .read_probe = adts_aac_probe, + .read_header = adts_aac_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "aac", + .raw_codec_id = AV_CODEC_ID_AAC, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aacdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aacdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/aacdec.o libavformat/aacdec.o: libavformat/aacdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/rawdec.h libavutil/opt.h libavformat/id3v1.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aacdec.o Binary file ffmpeg/libavformat/aacdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ac3dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ac3dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,123 @@ +/* + * RAW AC-3 and E-AC-3 demuxer + * Copyright (c) 2007 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/crc.h" +#include "libavcodec/ac3_parser.h" +#include "avformat.h" +#include "rawdec.h" + +static int ac3_eac3_probe(AVProbeData *p, enum AVCodecID expected_codec_id) +{ + int max_frames, first_frames = 0, frames; + const uint8_t *buf, *buf2, *end; + AC3HeaderInfo hdr; + GetBitContext gbc; + enum AVCodecID codec_id = AV_CODEC_ID_AC3; + + max_frames = 0; + buf = p->buf; + end = buf + p->buf_size; + + for(; buf < end; buf++) { + if(buf > p->buf && !(buf[0] == 0x0B && buf[1] == 0x77) + && !(buf[0] == 0x77 && buf[1] == 0x0B) ) + continue; + buf2 = buf; + + for(frames = 0; buf2 < end; frames++) { + uint8_t buf3[4096]; + int i; + if(!memcmp(buf2, "\x1\x10\0\0\0\0\0\0", 8)) + buf2+=16; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + for(i=0; i<8; i+=2) { + buf3[i ] = buf[i+1]; + buf3[i+1] = buf[i ]; + } + init_get_bits(&gbc, buf3, 54); + }else + init_get_bits(&gbc, buf2, 54); + if(avpriv_ac3_parse_header(&gbc, &hdr) < 0) + break; + if(buf2 + hdr.frame_size > end) + break; + if (buf[0] == 0x77 && buf[1] == 0x0B) { + av_assert0(hdr.frame_size <= sizeof(buf3)); + for(i=8; i 10) + codec_id = AV_CODEC_ID_EAC3; + buf2 += hdr.frame_size; + } + max_frames = FFMAX(max_frames, frames); + if(buf == p->buf) + first_frames = frames; + } + if(codec_id != expected_codec_id) return 0; + // keep this in sync with mp3 probe, both need to avoid + // issues with MPEG-files! + if (first_frames>=4) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>200)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=4) return AVPROBE_SCORE_MAX/4; + else if(max_frames>=1) return 1; + else return 0; +} + +#if CONFIG_AC3_DEMUXER +static int ac3_probe(AVProbeData *p) +{ + return ac3_eac3_probe(p, AV_CODEC_ID_AC3); +} + +AVInputFormat ff_ac3_demuxer = { + .name = "ac3", + .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), + .read_probe = ac3_probe, + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags= AVFMT_GENERIC_INDEX, + .extensions = "ac3", + .raw_codec_id = AV_CODEC_ID_AC3, +}; +#endif + +#if CONFIG_EAC3_DEMUXER +static int eac3_probe(AVProbeData *p) +{ + return ac3_eac3_probe(p, AV_CODEC_ID_EAC3); +} + +AVInputFormat ff_eac3_demuxer = { + .name = "eac3", + .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), + .read_probe = eac3_probe, + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "eac3", + .raw_codec_id = AV_CODEC_ID_EAC3, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ac3dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ac3dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +libavformat/ac3dec.o libavformat/ac3dec.o: libavformat/ac3dec.c \ + libavutil/crc.h libavutil/attributes.h libavcodec/ac3_parser.h \ + libavcodec/ac3.h libavutil/opt.h libavutil/rational.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavcodec/ac3tab.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ac3dec.o Binary file ffmpeg/libavformat/ac3dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/act.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/act.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,207 @@ +/* + * ACT file format demuxer + * Copyright (c) 2007-2008 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "riff.h" +#include "internal.h" +#include "libavcodec/get_bits.h" + +#define CHUNK_SIZE 512 +#define RIFF_TAG MKTAG('R','I','F','F') +#define WAVE_TAG MKTAG('W','A','V','E') + +typedef struct{ + int bytes_left_in_chunk; + uint8_t audio_buffer[22];///< temporary buffer for ACT frame + char second_packet; ///< 1 - if temporary buffer contains valid (second) G.729 packet +} ACTContext; + +static int probe(AVProbeData *p) +{ + int i; + + if ((AV_RL32(&p->buf[0]) != RIFF_TAG) || + (AV_RL32(&p->buf[8]) != WAVE_TAG) || + (AV_RL32(&p->buf[16]) != 16)) + return 0; + + //We cant be sure that this is ACT and not regular WAV + if (p->buf_size<512) + return 0; + + for(i=44; i<256; i++) + if(p->buf[i]) + return 0; + + if(p->buf[256]!=0x84) + return 0; + + for(i=264; i<512; i++) + if(p->buf[i]) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + ACTContext* ctx = s->priv_data; + AVIOContext *pb = s->pb; + int size; + AVStream* st; + + int min,sec,msec; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 16); + size=avio_rl32(pb); + ff_get_wav_header(pb, st->codec, size); + + /* + 8000Hz (Fine-rec) file format has 10 bytes long + packets with 10ms of sound data in them + */ + if (st->codec->sample_rate != 8000) { + av_log(s, AV_LOG_ERROR, "Sample rate %d is not supported.\n", st->codec->sample_rate); + return AVERROR_INVALIDDATA; + } + + st->codec->frame_size=80; + st->codec->channels=1; + avpriv_set_pts_info(st, 64, 1, 100); + + st->codec->codec_id=AV_CODEC_ID_G729; + + avio_seek(pb, 257, SEEK_SET); + msec=avio_rl16(pb); + sec=avio_r8(pb); + min=avio_rl32(pb); + + st->duration = av_rescale(1000*(min*60+sec)+msec, st->codec->sample_rate, 1000 * st->codec->frame_size); + + ctx->bytes_left_in_chunk=CHUNK_SIZE; + + avio_seek(pb, 512, SEEK_SET); + + return 0; +} + + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + ACTContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int frame_size=s->streams[0]->codec->sample_rate==8000?10:22; + + + if(s->streams[0]->codec->sample_rate==8000) + ret=av_new_packet(pkt, 10); + else + ret=av_new_packet(pkt, 11); + + if(ret) + return ret; + + if(s->streams[0]->codec->sample_rate==4400 && !ctx->second_packet) + { + ret = avio_read(pb, ctx->audio_buffer, frame_size); + + if(ret<0) + return ret; + if(ret!=frame_size) + return AVERROR(EIO); + + pkt->data[0]=ctx->audio_buffer[11]; + pkt->data[1]=ctx->audio_buffer[0]; + pkt->data[2]=ctx->audio_buffer[12]; + pkt->data[3]=ctx->audio_buffer[1]; + pkt->data[4]=ctx->audio_buffer[13]; + pkt->data[5]=ctx->audio_buffer[2]; + pkt->data[6]=ctx->audio_buffer[14]; + pkt->data[7]=ctx->audio_buffer[3]; + pkt->data[8]=ctx->audio_buffer[15]; + pkt->data[9]=ctx->audio_buffer[4]; + pkt->data[10]=ctx->audio_buffer[16]; + + ctx->second_packet=1; + } + else if(s->streams[0]->codec->sample_rate==4400 && ctx->second_packet) + { + pkt->data[0]=ctx->audio_buffer[5]; + pkt->data[1]=ctx->audio_buffer[17]; + pkt->data[2]=ctx->audio_buffer[6]; + pkt->data[3]=ctx->audio_buffer[18]; + pkt->data[4]=ctx->audio_buffer[7]; + pkt->data[5]=ctx->audio_buffer[19]; + pkt->data[6]=ctx->audio_buffer[8]; + pkt->data[7]=ctx->audio_buffer[20]; + pkt->data[8]=ctx->audio_buffer[9]; + pkt->data[9]=ctx->audio_buffer[21]; + pkt->data[10]=ctx->audio_buffer[10]; + + ctx->second_packet=0; + } + else // 8000 Hz + { + ret = avio_read(pb, ctx->audio_buffer, frame_size); + + if(ret<0) + return ret; + if(ret!=frame_size) + return AVERROR(EIO); + + pkt->data[0]=ctx->audio_buffer[5]; + pkt->data[1]=ctx->audio_buffer[0]; + pkt->data[2]=ctx->audio_buffer[6]; + pkt->data[3]=ctx->audio_buffer[1]; + pkt->data[4]=ctx->audio_buffer[7]; + pkt->data[5]=ctx->audio_buffer[2]; + pkt->data[6]=ctx->audio_buffer[8]; + pkt->data[7]=ctx->audio_buffer[3]; + pkt->data[8]=ctx->audio_buffer[9]; + pkt->data[9]=ctx->audio_buffer[4]; + } + + ctx->bytes_left_in_chunk -= frame_size; + + if(ctx->bytes_left_in_chunk < frame_size) + { + avio_skip(pb, ctx->bytes_left_in_chunk); + ctx->bytes_left_in_chunk=CHUNK_SIZE; + } + + pkt->duration=1; + + return ret; +} + +AVInputFormat ff_act_demuxer = { + .name = "act", + .long_name = "ACT Voice file format", + .priv_data_size = sizeof(ACTContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/act.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/act.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/act.o libavformat/act.o: libavformat/act.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/riff.h \ + libavformat/internal.h libavformat/metadata.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/act.o Binary file ffmpeg/libavformat/act.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adtsenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/adtsenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,175 @@ +/* + * ADTS muxer. + * Copyright (c) 2006 Baptiste Coudurier + * Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/mpeg4audio.h" +#include "avformat.h" + +#define ADTS_HEADER_SIZE 7 + +typedef struct { + int write_adts; + int objecttype; + int sample_rate_index; + int channel_conf; + int pce_size; + uint8_t pce_data[MAX_PCE_SIZE]; +} ADTSContext; + +#define ADTS_MAX_FRAME_BYTES ((1 << 13) - 1) + +static int adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, uint8_t *buf, int size) +{ + GetBitContext gb; + PutBitContext pb; + MPEG4AudioConfig m4ac; + int off; + + init_get_bits(&gb, buf, size * 8); + off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1); + if (off < 0) + return off; + skip_bits_long(&gb, off); + adts->objecttype = m4ac.object_type - 1; + adts->sample_rate_index = m4ac.sampling_index; + adts->channel_conf = m4ac.chan_config; + + if (adts->objecttype > 3U) { + av_log(s, AV_LOG_ERROR, "MPEG-4 AOT %d is not allowed in ADTS\n", adts->objecttype+1); + return -1; + } + if (adts->sample_rate_index == 15) { + av_log(s, AV_LOG_ERROR, "Escape sample rate index illegal in ADTS\n"); + return -1; + } + if (get_bits(&gb, 1)) { + av_log(s, AV_LOG_ERROR, "960/120 MDCT window is not allowed in ADTS\n"); + return -1; + } + if (get_bits(&gb, 1)) { + av_log(s, AV_LOG_ERROR, "Scalable configurations are not allowed in ADTS\n"); + return -1; + } + if (get_bits(&gb, 1)) { + av_log(s, AV_LOG_ERROR, "Extension flag is not allowed in ADTS\n"); + return -1; + } + if (!adts->channel_conf) { + init_put_bits(&pb, adts->pce_data, MAX_PCE_SIZE); + + put_bits(&pb, 3, 5); //ID_PCE + adts->pce_size = (avpriv_copy_pce_data(&pb, &gb) + 3) / 8; + flush_put_bits(&pb); + } + + adts->write_adts = 1; + + return 0; +} + +static int adts_write_header(AVFormatContext *s) +{ + ADTSContext *adts = s->priv_data; + AVCodecContext *avc = s->streams[0]->codec; + + if (avc->extradata_size > 0 && + adts_decode_extradata(s, adts, avc->extradata, avc->extradata_size) < 0) + return -1; + + return 0; +} + +static int adts_write_frame_header(ADTSContext *ctx, + uint8_t *buf, int size, int pce_size) +{ + PutBitContext pb; + + unsigned full_frame_size = (unsigned)ADTS_HEADER_SIZE + size + pce_size; + if (full_frame_size > ADTS_MAX_FRAME_BYTES) { + av_log(NULL, AV_LOG_ERROR, "ADTS frame size too large: %u (max %d)\n", + full_frame_size, ADTS_MAX_FRAME_BYTES); + return AVERROR_INVALIDDATA; + } + + init_put_bits(&pb, buf, ADTS_HEADER_SIZE); + + /* adts_fixed_header */ + put_bits(&pb, 12, 0xfff); /* syncword */ + put_bits(&pb, 1, 0); /* ID */ + put_bits(&pb, 2, 0); /* layer */ + put_bits(&pb, 1, 1); /* protection_absent */ + put_bits(&pb, 2, ctx->objecttype); /* profile_objecttype */ + put_bits(&pb, 4, ctx->sample_rate_index); + put_bits(&pb, 1, 0); /* private_bit */ + put_bits(&pb, 3, ctx->channel_conf); /* channel_configuration */ + put_bits(&pb, 1, 0); /* original_copy */ + put_bits(&pb, 1, 0); /* home */ + + /* adts_variable_header */ + put_bits(&pb, 1, 0); /* copyright_identification_bit */ + put_bits(&pb, 1, 0); /* copyright_identification_start */ + put_bits(&pb, 13, full_frame_size); /* aac_frame_length */ + put_bits(&pb, 11, 0x7ff); /* adts_buffer_fullness */ + put_bits(&pb, 2, 0); /* number_of_raw_data_blocks_in_frame */ + + flush_put_bits(&pb); + + return 0; +} + +static int adts_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + ADTSContext *adts = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t buf[ADTS_HEADER_SIZE]; + + if (!pkt->size) + return 0; + if (adts->write_adts) { + int err = adts_write_frame_header(adts, buf, pkt->size, + adts->pce_size); + if (err < 0) + return err; + avio_write(pb, buf, ADTS_HEADER_SIZE); + if (adts->pce_size) { + avio_write(pb, adts->pce_data, adts->pce_size); + adts->pce_size = 0; + } + } + avio_write(pb, pkt->data, pkt->size); + + return 0; +} + +AVOutputFormat ff_adts_muxer = { + .name = "adts", + .long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"), + .mime_type = "audio/aac", + .extensions = "aac,adts", + .priv_data_size = sizeof(ADTSContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_NONE, + .write_header = adts_write_header, + .write_packet = adts_write_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adtsenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/adtsenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/adtsenc.o libavformat/adtsenc.o: libavformat/adtsenc.c \ + libavcodec/get_bits.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/put_bits.h libavutil/bswap.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavcodec/mpeg4audio.h \ + libavcodec/get_bits.h libavcodec/put_bits.h libavformat/avformat.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adtsenc.o Binary file ffmpeg/libavformat/adtsenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adxdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/adxdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * CRI ADX demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/adx.h" +#include "avformat.h" +#include "internal.h" + +#define BLOCK_SIZE 18 +#define BLOCK_SAMPLES 32 + +typedef struct ADXDemuxerContext { + int header_size; +} ADXDemuxerContext; + +static int adx_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ADXDemuxerContext *c = s->priv_data; + AVCodecContext *avctx = s->streams[0]->codec; + int ret, size; + + size = BLOCK_SIZE * avctx->channels; + + pkt->pos = avio_tell(s->pb); + pkt->stream_index = 0; + + ret = av_get_packet(s->pb, pkt, size); + if (ret != size) { + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR(EIO); + } + if (AV_RB16(pkt->data) & 0x8000) { + av_free_packet(pkt); + return AVERROR_EOF; + } + pkt->size = size; + pkt->duration = 1; + pkt->pts = (pkt->pos - c->header_size) / size; + + return 0; +} + +static int adx_read_header(AVFormatContext *s) +{ + ADXDemuxerContext *c = s->priv_data; + AVCodecContext *avctx; + int ret; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avctx = s->streams[0]->codec; + + if (avio_rb16(s->pb) != 0x8000) + return AVERROR_INVALIDDATA; + c->header_size = avio_rb16(s->pb) + 4; + avio_seek(s->pb, -4, SEEK_CUR); + + avctx->extradata = av_mallocz(c->header_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + if (avio_read(s->pb, avctx->extradata, c->header_size) < c->header_size) { + av_freep(&avctx->extradata); + return AVERROR(EIO); + } + avctx->extradata_size = c->header_size; + + ret = avpriv_adx_decode_header(avctx, avctx->extradata, + avctx->extradata_size, &c->header_size, + NULL); + if (ret) + return ret; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + + avpriv_set_pts_info(st, 64, BLOCK_SAMPLES, avctx->sample_rate); + + return 0; +} + +AVInputFormat ff_adx_demuxer = { + .name = "adx", + .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), + .priv_data_size = sizeof(ADXDemuxerContext), + .read_header = adx_read_header, + .read_packet = adx_read_packet, + .extensions = "adx", + .raw_codec_id = AV_CODEC_ID_ADPCM_ADX, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adxdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/adxdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/adxdec.o libavformat/adxdec.o: libavformat/adxdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavcodec/adx.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/adxdec.o Binary file ffmpeg/libavformat/adxdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aea.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aea.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,105 @@ +/* + * MD STUDIO audio demuxer + * + * Copyright (c) 2009 Benjamin Larsson + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "pcm.h" + +#define AT1_SU_SIZE 212 + +static int aea_read_probe(AVProbeData *p) +{ + if (p->buf_size <= 2048+212) + return 0; + + /* Magic is '00 08 00 00' in Little Endian*/ + if (AV_RL32(p->buf)==0x800) { + int bsm_s, bsm_e, inb_s, inb_e, ch; + ch = p->buf[264]; + bsm_s = p->buf[2048]; + inb_s = p->buf[2048+1]; + inb_e = p->buf[2048+210]; + bsm_e = p->buf[2048+211]; + + if (ch != 1 && ch != 2) + return 0; + + /* Check so that the redundant bsm bytes and info bytes are valid + * the block size mode bytes have to be the same + * the info bytes have to be the same + */ + if (bsm_s == bsm_e && inb_s == inb_e) + return AVPROBE_SCORE_MAX / 4 + 1; + } + return 0; +} + +static int aea_read_header(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + /* Parse the amount of channels and skip to pos 2048(0x800) */ + avio_skip(s->pb, 264); + st->codec->channels = avio_r8(s->pb); + avio_skip(s->pb, 1783); + + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ATRAC1; + st->codec->sample_rate = 44100; + st->codec->bit_rate = 292000; + + if (st->codec->channels != 1 && st->codec->channels != 2) { + av_log(s,AV_LOG_ERROR,"Channels %d not supported!\n",st->codec->channels); + return -1; + } + + st->codec->channel_layout = (st->codec->channels == 1) ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; + + st->codec->block_align = AT1_SU_SIZE * st->codec->channels; + return 0; +} + +static int aea_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret = av_get_packet(s->pb, pkt, s->streams[0]->codec->block_align); + + pkt->stream_index = 0; + if (ret <= 0) + return AVERROR(EIO); + + return ret; +} + +AVInputFormat ff_aea_demuxer = { + .name = "aea", + .long_name = NULL_IF_CONFIG_SMALL("MD STUDIO audio"), + .read_probe = aea_read_probe, + .read_header = aea_read_header, + .read_packet = aea_read_packet, + .read_seek = ff_pcm_read_seek, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "aea", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aea.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aea.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/aea.o libavformat/aea.o: libavformat/aea.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aea.o Binary file ffmpeg/libavformat/aea.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/afc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/afc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,81 @@ +/* + * AFC demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +typedef struct AFCDemuxContext { + int64_t data_end; +} AFCDemuxContext; + +static int afc_read_header(AVFormatContext *s) +{ + AFCDemuxContext *c = s->priv_data; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_AFC; + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->extradata_size = 1; + + st->codec->extradata = av_mallocz(1 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 8 * st->codec->channels; + + c->data_end = avio_rb32(s->pb) + 32LL; + st->duration = avio_rb32(s->pb); + st->codec->sample_rate = avio_rb16(s->pb); + avio_skip(s->pb, 22); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int afc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AFCDemuxContext *c = s->priv_data; + int64_t size; + int ret; + + size = FFMIN(c->data_end - avio_tell(s->pb), 18 * 128); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 0; + return ret; +} + +AVInputFormat ff_afc_demuxer = { + .name = "afc", + .long_name = NULL_IF_CONFIG_SMALL("AFC"), + .priv_data_size = sizeof(AFCDemuxContext), + .read_header = afc_read_header, + .read_packet = afc_read_packet, + .extensions = "afc", + .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/afc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/afc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/afc.o libavformat/afc.o: libavformat/afc.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/afc.o Binary file ffmpeg/libavformat/afc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiff.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aiff.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,57 @@ +/* + * AIFF/AIFF-C muxer/demuxer common header + * Copyright (c) 2006 Patrick Guimond + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * common header for AIFF muxer and demuxer + */ + +#ifndef AVFORMAT_AIFF_H +#define AVFORMAT_AIFF_H + +#include "avformat.h" +#include "internal.h" + +static const AVCodecTag ff_codec_aiff_tags[] = { + { AV_CODEC_ID_PCM_S16BE, MKTAG('N','O','N','E') }, + { AV_CODEC_ID_PCM_S8, MKTAG('N','O','N','E') }, + { AV_CODEC_ID_PCM_U8, MKTAG('r','a','w',' ') }, + { AV_CODEC_ID_PCM_S24BE, MKTAG('N','O','N','E') }, + { AV_CODEC_ID_PCM_S32BE, MKTAG('N','O','N','E') }, + { AV_CODEC_ID_PCM_F32BE, MKTAG('f','l','3','2') }, + { AV_CODEC_ID_PCM_F64BE, MKTAG('f','l','6','4') }, + { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') }, + { AV_CODEC_ID_PCM_S24BE, MKTAG('i','n','2','4') }, + { AV_CODEC_ID_PCM_S32BE, MKTAG('i','n','3','2') }, + { AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') }, + { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') }, + { AV_CODEC_ID_GSM, MKTAG('G','S','M',' ') }, + { AV_CODEC_ID_ADPCM_G726, MKTAG('G','7','2','6') }, + { AV_CODEC_ID_PCM_S16BE, MKTAG('t','w','o','s') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('s','o','w','t') }, + { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') }, + { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +#endif /* AVFORMAT_AIFF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aiffdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,361 @@ +/* + * AIFF/AIFF-C demuxer + * Copyright (c) 2006 Patrick Guimond + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/dict.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" +#include "aiff.h" +#include "isom.h" +#include "id3v2.h" +#include "mov_chan.h" + +#define AIFF 0 +#define AIFF_C_VERSION1 0xA2805140 + +typedef struct { + int64_t data_end; + int block_duration; +} AIFFInputContext; + +static enum AVCodecID aiff_codec_get_id(int bps) +{ + if (bps <= 8) + return AV_CODEC_ID_PCM_S8; + if (bps <= 16) + return AV_CODEC_ID_PCM_S16BE; + if (bps <= 24) + return AV_CODEC_ID_PCM_S24BE; + if (bps <= 32) + return AV_CODEC_ID_PCM_S32BE; + + /* bigger than 32 isn't allowed */ + return AV_CODEC_ID_NONE; +} + +/* returns the size of the found tag */ +static int get_tag(AVIOContext *pb, uint32_t * tag) +{ + int size; + + if (url_feof(pb)) + return AVERROR(EIO); + + *tag = avio_rl32(pb); + size = avio_rb32(pb); + + if (size < 0) + size = 0x7fffffff; + + return size; +} + +/* Metadata string read */ +static void get_meta(AVFormatContext *s, const char *key, int size) +{ + uint8_t *str = av_malloc(size+1); + + if (str) { + int res = avio_read(s->pb, str, size); + if (res < 0){ + av_free(str); + return; + } + size += (size&1)-res; + str[res] = 0; + av_dict_set(&s->metadata, key, str, AV_DICT_DONT_STRDUP_VAL); + }else + size+= size&1; + + avio_skip(s->pb, size); +} + +/* Returns the number of sound data frames or negative on error */ +static unsigned int get_aiff_header(AVFormatContext *s, int size, + unsigned version) +{ + AVIOContext *pb = s->pb; + AVCodecContext *codec = s->streams[0]->codec; + AIFFInputContext *aiff = s->priv_data; + int exp; + uint64_t val; + double sample_rate; + unsigned int num_frames; + + if (size & 1) + size++; + codec->codec_type = AVMEDIA_TYPE_AUDIO; + codec->channels = avio_rb16(pb); + num_frames = avio_rb32(pb); + codec->bits_per_coded_sample = avio_rb16(pb); + + exp = avio_rb16(pb); + val = avio_rb64(pb); + sample_rate = ldexp(val, exp - 16383 - 63); + codec->sample_rate = sample_rate; + size -= 18; + + /* get codec id for AIFF-C */ + if (version == AIFF_C_VERSION1) { + codec->codec_tag = avio_rl32(pb); + codec->codec_id = ff_codec_get_id(ff_codec_aiff_tags, codec->codec_tag); + size -= 4; + } + + if (version != AIFF_C_VERSION1 || codec->codec_id == AV_CODEC_ID_PCM_S16BE) { + codec->codec_id = aiff_codec_get_id(codec->bits_per_coded_sample); + codec->bits_per_coded_sample = av_get_bits_per_sample(codec->codec_id); + aiff->block_duration = 1; + } else { + switch (codec->codec_id) { + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + aiff->block_duration = 1; + break; + case AV_CODEC_ID_ADPCM_IMA_QT: + codec->block_align = 34*codec->channels; + break; + case AV_CODEC_ID_MACE3: + codec->block_align = 2*codec->channels; + break; + case AV_CODEC_ID_MACE6: + codec->block_align = 1*codec->channels; + break; + case AV_CODEC_ID_GSM: + codec->block_align = 33; + break; + case AV_CODEC_ID_QCELP: + codec->block_align = 35; + break; + default: + aiff->block_duration = 1; + break; + } + if (codec->block_align > 0) + aiff->block_duration = av_get_audio_frame_duration(codec, + codec->block_align); + } + + /* Block align needs to be computed in all cases, as the definition + * is specific to applications -> here we use the WAVE format definition */ + if (!codec->block_align) + codec->block_align = (av_get_bits_per_sample(codec->codec_id) * codec->channels) >> 3; + + if (aiff->block_duration) { + codec->bit_rate = codec->sample_rate * (codec->block_align << 3) / + aiff->block_duration; + } + + /* Chunk is over */ + if (size) + avio_skip(pb, size); + + return num_frames; +} + +static int aiff_probe(AVProbeData *p) +{ + /* check file header */ + if (p->buf[0] == 'F' && p->buf[1] == 'O' && + p->buf[2] == 'R' && p->buf[3] == 'M' && + p->buf[8] == 'A' && p->buf[9] == 'I' && + p->buf[10] == 'F' && (p->buf[11] == 'F' || p->buf[11] == 'C')) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +/* aiff input */ +static int aiff_read_header(AVFormatContext *s) +{ + int ret, size, filesize; + int64_t offset = 0, position; + uint32_t tag; + unsigned version = AIFF_C_VERSION1; + AVIOContext *pb = s->pb; + AVStream * st; + AIFFInputContext *aiff = s->priv_data; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + + /* check FORM header */ + filesize = get_tag(pb, &tag); + if (filesize < 0 || tag != MKTAG('F', 'O', 'R', 'M')) + return AVERROR_INVALIDDATA; + + /* AIFF data type */ + tag = avio_rl32(pb); + if (tag == MKTAG('A', 'I', 'F', 'F')) /* Got an AIFF file */ + version = AIFF; + else if (tag != MKTAG('A', 'I', 'F', 'C')) /* An AIFF-C file then */ + return AVERROR_INVALIDDATA; + + filesize -= 4; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + while (filesize > 0) { + /* parse different chunks */ + size = get_tag(pb, &tag); + if (size < 0) + return size; + + filesize -= size + 8; + + switch (tag) { + case MKTAG('C', 'O', 'M', 'M'): /* Common chunk */ + /* Then for the complete header info */ + st->nb_frames = get_aiff_header(s, size, version); + if (st->nb_frames < 0) + return st->nb_frames; + if (offset > 0) // COMM is after SSND + goto got_sound; + break; + case MKTAG('I', 'D', '3', ' '): + position = avio_tell(pb); + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) + if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) { + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + return ret; + } + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + if (position + size > avio_tell(pb)) + avio_skip(pb, position + size - avio_tell(pb)); + break; + case MKTAG('F', 'V', 'E', 'R'): /* Version chunk */ + version = avio_rb32(pb); + break; + case MKTAG('N', 'A', 'M', 'E'): /* Sample name chunk */ + get_meta(s, "title" , size); + break; + case MKTAG('A', 'U', 'T', 'H'): /* Author chunk */ + get_meta(s, "author" , size); + break; + case MKTAG('(', 'c', ')', ' '): /* Copyright chunk */ + get_meta(s, "copyright", size); + break; + case MKTAG('A', 'N', 'N', 'O'): /* Annotation chunk */ + get_meta(s, "comment" , size); + break; + case MKTAG('S', 'S', 'N', 'D'): /* Sampled sound chunk */ + aiff->data_end = avio_tell(pb) + size; + offset = avio_rb32(pb); /* Offset of sound data */ + avio_rb32(pb); /* BlockSize... don't care */ + offset += avio_tell(pb); /* Compute absolute data offset */ + if (st->codec->block_align && !pb->seekable) /* Assume COMM already parsed */ + goto got_sound; + if (!pb->seekable) { + av_log(s, AV_LOG_ERROR, "file is not seekable\n"); + return -1; + } + avio_skip(pb, size - 8); + break; + case MKTAG('w', 'a', 'v', 'e'): + if ((uint64_t)size > (1<<30)) + return -1; + st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = size; + avio_read(pb, st->codec->extradata, size); + if (st->codec->codec_id == AV_CODEC_ID_QDM2 && size>=12*4 && !st->codec->block_align) { + st->codec->block_align = AV_RB32(st->codec->extradata+11*4); + aiff->block_duration = AV_RB32(st->codec->extradata+9*4); + } + break; + case MKTAG('C','H','A','N'): + if(ff_mov_read_chan(s, pb, st, size) < 0) + return AVERROR_INVALIDDATA; + break; + default: /* Jump */ + if (size & 1) /* Always even aligned */ + size++; + avio_skip(pb, size); + } + } + +got_sound: + if (!st->codec->block_align) { + av_log(s, AV_LOG_ERROR, "could not find COMM tag or invalid block_align value\n"); + return -1; + } + + /* Now positioned, get the sound data start and end */ + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + st->start_time = 0; + st->duration = st->nb_frames * aiff->block_duration; + + /* Position the stream at the first block */ + avio_seek(pb, offset, SEEK_SET); + + return 0; +} + +#define MAX_SIZE 4096 + +static int aiff_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVStream *st = s->streams[0]; + AIFFInputContext *aiff = s->priv_data; + int64_t max_size; + int res, size; + + /* calculate size of remaining data */ + max_size = aiff->data_end - avio_tell(s->pb); + if (max_size <= 0) + return AVERROR_EOF; + + /* Now for that packet */ + if (st->codec->block_align >= 33) // GSM, QCLP, IMA4 + size = st->codec->block_align; + else + size = (MAX_SIZE / st->codec->block_align) * st->codec->block_align; + size = FFMIN(max_size, size); + res = av_get_packet(s->pb, pkt, size); + if (res < 0) + return res; + + if (size >= st->codec->block_align) + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + /* Only one stream in an AIFF file */ + pkt->stream_index = 0; + pkt->duration = (res / st->codec->block_align) * aiff->block_duration; + return 0; +} + +AVInputFormat ff_aiff_demuxer = { + .name = "aiff", + .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), + .priv_data_size = sizeof(AIFFInputContext), + .read_probe = aiff_probe, + .read_header = aiff_read_header, + .read_packet = aiff_read_packet, + .read_seek = ff_pcm_read_seek, + .codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aiffdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/aiffdec.o libavformat/aiffdec.o: libavformat/aiffdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/dict.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h libavformat/aiff.h libavformat/isom.h \ + libavformat/dv.h libavformat/id3v2.h libavformat/metadata.h \ + libavformat/mov_chan.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffdec.o Binary file ffmpeg/libavformat/aiffdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aiffenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,332 @@ +/* + * AIFF/AIFF-C muxer + * Copyright (c) 2006 Patrick Guimond + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intfloat.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" +#include "aiff.h" +#include "avio_internal.h" +#include "isom.h" +#include "id3v2.h" + +typedef struct { + const AVClass *class; + int64_t form; + int64_t frames; + int64_t ssnd; + int audio_stream_idx; + AVPacketList *pict_list; + int write_id3v2; + int id3v2_version; +} AIFFOutputContext; + +static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff) +{ + int ret; + uint64_t pos, end, size; + ID3v2EncContext id3v2 = { 0 }; + AVIOContext *pb = s->pb; + AVPacketList *pict_list = aiff->pict_list; + + if (!pb->seekable) + return 0; + + if (!s->metadata && !aiff->pict_list) + return 0; + + avio_wl32(pb, MKTAG('I', 'D', '3', ' ')); + avio_wb32(pb, 0); + pos = avio_tell(pb); + + ff_id3v2_start(&id3v2, pb, aiff->id3v2_version, ID3v2_DEFAULT_MAGIC); + ff_id3v2_write_metadata(s, &id3v2); + while (pict_list) { + if ((ret = ff_id3v2_write_apic(s, &id3v2, &pict_list->pkt)) < 0) + return ret; + pict_list = pict_list->next; + } + ff_id3v2_finish(&id3v2, pb); + + end = avio_tell(pb); + size = end - pos; + + /* Update chunk size */ + avio_seek(pb, pos - 4, SEEK_SET); + avio_wb32(pb, size); + avio_seek(pb, end, SEEK_SET); + + if (size & 1) + avio_w8(pb, 0); + + return 0; +} + +static void put_meta(AVFormatContext *s, const char *key, uint32_t id) +{ + AVDictionaryEntry *tag; + AVIOContext *pb = s->pb; + + if (tag = av_dict_get(s->metadata, key, NULL, 0)) { + int size = strlen(tag->value); + + avio_wl32(pb, id); + avio_wb32(pb, FFALIGN(size, 2)); + avio_write(pb, tag->value, size); + if (size & 1) + avio_w8(pb, 0); + } +} + +static int aiff_write_header(AVFormatContext *s) +{ + AIFFOutputContext *aiff = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc; + uint64_t sample_rate; + int i, aifc = 0; + + aiff->audio_stream_idx = -1; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (aiff->audio_stream_idx < 0 && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + aiff->audio_stream_idx = i; + } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) { + av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in AIFF.\n"); + return AVERROR(EINVAL); + } + } + if (aiff->audio_stream_idx < 0) { + av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); + return AVERROR(EINVAL); + } + + enc = s->streams[aiff->audio_stream_idx]->codec; + + /* First verify if format is ok */ + if (!enc->codec_tag) + return -1; + if (enc->codec_tag != MKTAG('N','O','N','E')) + aifc = 1; + + /* FORM AIFF header */ + ffio_wfourcc(pb, "FORM"); + aiff->form = avio_tell(pb); + avio_wb32(pb, 0); /* file length */ + ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF"); + + if (aifc) { // compressed audio + if (!enc->block_align) { + av_log(s, AV_LOG_ERROR, "block align not set\n"); + return -1; + } + /* Version chunk */ + ffio_wfourcc(pb, "FVER"); + avio_wb32(pb, 4); + avio_wb32(pb, 0xA2805140); + } + + if (enc->channels > 2 && enc->channel_layout) { + ffio_wfourcc(pb, "CHAN"); + avio_wb32(pb, 12); + ff_mov_write_chan(pb, enc->channel_layout); + } + + put_meta(s, "title", MKTAG('N', 'A', 'M', 'E')); + put_meta(s, "author", MKTAG('A', 'U', 'T', 'H')); + put_meta(s, "copyright", MKTAG('(', 'c', ')', ' ')); + put_meta(s, "comment", MKTAG('A', 'N', 'N', 'O')); + + /* Common chunk */ + ffio_wfourcc(pb, "COMM"); + avio_wb32(pb, aifc ? 24 : 18); /* size */ + avio_wb16(pb, enc->channels); /* Number of channels */ + + aiff->frames = avio_tell(pb); + avio_wb32(pb, 0); /* Number of frames */ + + if (!enc->bits_per_coded_sample) + enc->bits_per_coded_sample = av_get_bits_per_sample(enc->codec_id); + if (!enc->bits_per_coded_sample) { + av_log(s, AV_LOG_ERROR, "could not compute bits per sample\n"); + return -1; + } + if (!enc->block_align) + enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3; + + avio_wb16(pb, enc->bits_per_coded_sample); /* Sample size */ + + sample_rate = av_double2int(enc->sample_rate); + avio_wb16(pb, (sample_rate >> 52) + (16383 - 1023)); + avio_wb64(pb, UINT64_C(1) << 63 | sample_rate << 11); + + if (aifc) { + avio_wl32(pb, enc->codec_tag); + avio_wb16(pb, 0); + } + + if (enc->codec_tag == MKTAG('Q','D','M','2') && enc->extradata_size) { + ffio_wfourcc(pb, "wave"); + avio_wb32(pb, enc->extradata_size); + avio_write(pb, enc->extradata, enc->extradata_size); + } + + /* Sound data chunk */ + ffio_wfourcc(pb, "SSND"); + aiff->ssnd = avio_tell(pb); /* Sound chunk size */ + avio_wb32(pb, 0); /* Sound samples data size */ + avio_wb32(pb, 0); /* Data offset */ + avio_wb32(pb, 0); /* Block-size (block align) */ + + avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1, + s->streams[aiff->audio_stream_idx]->codec->sample_rate); + + /* Data is starting here */ + avio_flush(pb); + + return 0; +} + +static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AIFFOutputContext *aiff = s->priv_data; + AVIOContext *pb = s->pb; + if (pkt->stream_index == aiff->audio_stream_idx) + avio_write(pb, pkt->data, pkt->size); + else { + int ret; + AVPacketList *pict_list, *last; + + if (s->streams[pkt->stream_index]->codec->codec_type != AVMEDIA_TYPE_VIDEO) + return 0; + + /* warn only once for each stream */ + if (s->streams[pkt->stream_index]->nb_frames == 1) { + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + } + if (s->streams[pkt->stream_index]->nb_frames >= 1) + return 0; + + pict_list = av_mallocz(sizeof(AVPacketList)); + if (!pict_list) + return AVERROR(ENOMEM); + + if ((ret = av_copy_packet(&pict_list->pkt, pkt)) < 0) { + av_freep(&pict_list); + return ret; + } + + if (!aiff->pict_list) + aiff->pict_list = pict_list; + else { + last = aiff->pict_list; + while (last->next) + last = last->next; + last->next = pict_list; + } + } + + return 0; +} + +static int aiff_write_trailer(AVFormatContext *s) +{ + int ret; + AVIOContext *pb = s->pb; + AIFFOutputContext *aiff = s->priv_data; + AVPacketList *pict_list = aiff->pict_list; + AVCodecContext *enc = s->streams[aiff->audio_stream_idx]->codec; + + /* Chunks sizes must be even */ + int64_t file_size, end_size; + end_size = file_size = avio_tell(pb); + if (file_size & 1) { + avio_w8(pb, 0); + end_size++; + } + + if (s->pb->seekable) { + /* Number of sample frames */ + avio_seek(pb, aiff->frames, SEEK_SET); + avio_wb32(pb, (file_size-aiff->ssnd-12)/enc->block_align); + + /* Sound Data chunk size */ + avio_seek(pb, aiff->ssnd, SEEK_SET); + avio_wb32(pb, file_size - aiff->ssnd - 4); + + /* return to the end */ + avio_seek(pb, end_size, SEEK_SET); + + /* Write ID3 tags */ + if (aiff->write_id3v2) + if ((ret = put_id3v2_tags(s, aiff)) < 0) + return ret; + + /* File length */ + file_size = avio_tell(pb); + avio_seek(pb, aiff->form, SEEK_SET); + avio_wb32(pb, file_size - aiff->form - 4); + + avio_flush(pb); + } + + while (pict_list) { + AVPacketList *next = pict_list->next; + av_free_packet(&pict_list->pkt); + av_freep(&pict_list); + pict_list = next; + } + + return 0; +} + +#define OFFSET(x) offsetof(AIFFOutputContext, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "write_id3v2", "Enable ID3 tags writing.", + OFFSET(write_id3v2), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC }, + { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", + OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC }, + { NULL }, +}; + +static const AVClass aiff_muxer_class = { + .class_name = "AIFF muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_aiff_muxer = { + .name = "aiff", + .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), + .mime_type = "audio/aiff", + .extensions = "aif,aiff,afc,aifc", + .priv_data_size = sizeof(AIFFOutputContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE, + .video_codec = AV_CODEC_ID_PNG, + .write_header = aiff_write_header, + .write_packet = aiff_write_packet, + .write_trailer = aiff_write_trailer, + .codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 }, + .priv_class = &aiff_muxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aiffenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/aiffenc.o libavformat/aiffenc.o: libavformat/aiffenc.c \ + libavutil/intfloat.h libavutil/attributes.h libavutil/opt.h \ + libavutil/rational.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/aiff.h libavformat/avio_internal.h libavformat/url.h \ + libavformat/isom.h libavformat/dv.h libavformat/id3v2.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aiffenc.o Binary file ffmpeg/libavformat/aiffenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/allformats.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/allformats.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,342 @@ +/* + * Register all the formats and protocols + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rtp.h" +#include "rdt.h" +#include "url.h" +#include "version.h" + +#define REGISTER_MUXER(X, x) \ + { \ + extern AVOutputFormat ff_##x##_muxer; \ + if (CONFIG_##X##_MUXER) \ + av_register_output_format(&ff_##x##_muxer); \ + } + +#define REGISTER_DEMUXER(X, x) \ + { \ + extern AVInputFormat ff_##x##_demuxer; \ + if (CONFIG_##X##_DEMUXER) \ + av_register_input_format(&ff_##x##_demuxer); \ + } + +#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x) + +#define REGISTER_PROTOCOL(X, x) \ + { \ + extern URLProtocol ff_##x##_protocol; \ + if (CONFIG_##X##_PROTOCOL) \ + ffurl_register_protocol(&ff_##x##_protocol, \ + sizeof(ff_##x##_protocol)); \ + } + +void av_register_all(void) +{ + static int initialized; + + if (initialized) + return; + initialized = 1; + + avcodec_register_all(); + + /* (de)muxers */ + REGISTER_MUXER (A64, a64); + REGISTER_DEMUXER (AAC, aac); + REGISTER_MUXDEMUX(AC3, ac3); + REGISTER_DEMUXER (ACT, act); + REGISTER_DEMUXER (ADF, adf); + REGISTER_MUXER (ADTS, adts); + REGISTER_MUXDEMUX(ADX, adx); + REGISTER_DEMUXER (AEA, aea); + REGISTER_DEMUXER (AFC, afc); + REGISTER_MUXDEMUX(AIFF, aiff); + REGISTER_MUXDEMUX(AMR, amr); + REGISTER_DEMUXER (ANM, anm); + REGISTER_DEMUXER (APC, apc); + REGISTER_DEMUXER (APE, ape); + REGISTER_DEMUXER (AQTITLE, aqtitle); + REGISTER_MUXDEMUX(ASF, asf); + REGISTER_MUXDEMUX(ASS, ass); + REGISTER_MUXDEMUX(AST, ast); + REGISTER_MUXER (ASF_STREAM, asf_stream); + REGISTER_MUXDEMUX(AU, au); + REGISTER_MUXDEMUX(AVI, avi); + REGISTER_DEMUXER (AVISYNTH, avisynth); + REGISTER_MUXER (AVM2, avm2); + REGISTER_DEMUXER (AVR, avr); + REGISTER_DEMUXER (AVS, avs); + REGISTER_DEMUXER (BETHSOFTVID, bethsoftvid); + REGISTER_DEMUXER (BFI, bfi); + REGISTER_DEMUXER (BINTEXT, bintext); + REGISTER_DEMUXER (BINK, bink); + REGISTER_MUXDEMUX(BIT, bit); + REGISTER_DEMUXER (BMV, bmv); + REGISTER_DEMUXER (BRSTM, brstm); + REGISTER_DEMUXER (C93, c93); + REGISTER_MUXDEMUX(CAF, caf); + REGISTER_MUXDEMUX(CAVSVIDEO, cavsvideo); + REGISTER_DEMUXER (CDG, cdg); + REGISTER_DEMUXER (CDXL, cdxl); + REGISTER_DEMUXER (CONCAT, concat); + REGISTER_MUXER (CRC, crc); + REGISTER_MUXDEMUX(DAUD, daud); + REGISTER_DEMUXER (DFA, dfa); + REGISTER_MUXDEMUX(DIRAC, dirac); + REGISTER_MUXDEMUX(DNXHD, dnxhd); + REGISTER_DEMUXER (DSICIN, dsicin); + REGISTER_MUXDEMUX(DTS, dts); + REGISTER_DEMUXER (DTSHD, dtshd); + REGISTER_MUXDEMUX(DV, dv); + REGISTER_DEMUXER (DXA, dxa); + REGISTER_DEMUXER (EA, ea); + REGISTER_DEMUXER (EA_CDATA, ea_cdata); + REGISTER_MUXDEMUX(EAC3, eac3); + REGISTER_DEMUXER (EPAF, epaf); + REGISTER_MUXER (F4V, f4v); + REGISTER_MUXDEMUX(FFM, ffm); + REGISTER_MUXDEMUX(FFMETADATA, ffmetadata); + REGISTER_MUXDEMUX(FILMSTRIP, filmstrip); + REGISTER_MUXDEMUX(FLAC, flac); + REGISTER_DEMUXER (FLIC, flic); + REGISTER_MUXDEMUX(FLV, flv); + REGISTER_DEMUXER (FOURXM, fourxm); + REGISTER_MUXER (FRAMECRC, framecrc); + REGISTER_MUXER (FRAMEMD5, framemd5); + REGISTER_DEMUXER (FRM, frm); + REGISTER_MUXDEMUX(G722, g722); + REGISTER_MUXDEMUX(G723_1, g723_1); + REGISTER_DEMUXER (G729, g729); + REGISTER_MUXDEMUX(GIF, gif); + REGISTER_DEMUXER (GSM, gsm); + REGISTER_MUXDEMUX(GXF, gxf); + REGISTER_MUXDEMUX(H261, h261); + REGISTER_MUXDEMUX(H263, h263); + REGISTER_MUXDEMUX(H264, h264); + REGISTER_MUXDEMUX(HLS, hls); + REGISTER_MUXDEMUX(ICO, ico); + REGISTER_DEMUXER (IDCIN, idcin); + REGISTER_DEMUXER (IDF, idf); + REGISTER_DEMUXER (IFF, iff); + REGISTER_MUXDEMUX(ILBC, ilbc); + REGISTER_MUXDEMUX(IMAGE2, image2); + REGISTER_MUXDEMUX(IMAGE2PIPE, image2pipe); + REGISTER_DEMUXER (INGENIENT, ingenient); + REGISTER_DEMUXER (IPMOVIE, ipmovie); + REGISTER_MUXER (IPOD, ipod); + REGISTER_MUXDEMUX(IRCAM, ircam); + REGISTER_MUXER (ISMV, ismv); + REGISTER_DEMUXER (ISS, iss); + REGISTER_DEMUXER (IV8, iv8); + REGISTER_MUXDEMUX(IVF, ivf); + REGISTER_MUXDEMUX(JACOSUB, jacosub); + REGISTER_DEMUXER (JV, jv); + REGISTER_MUXDEMUX(LATM, latm); + REGISTER_DEMUXER (LMLM4, lmlm4); + REGISTER_DEMUXER (LOAS, loas); + REGISTER_DEMUXER (LVF, lvf); + REGISTER_DEMUXER (LXF, lxf); + REGISTER_MUXDEMUX(M4V, m4v); + REGISTER_MUXER (MD5, md5); + REGISTER_MUXDEMUX(MATROSKA, matroska); + REGISTER_MUXER (MATROSKA_AUDIO, matroska_audio); + REGISTER_DEMUXER (MGSTS, mgsts); + REGISTER_MUXDEMUX(MICRODVD, microdvd); + REGISTER_MUXDEMUX(MJPEG, mjpeg); + REGISTER_MUXDEMUX(MLP, mlp); + REGISTER_DEMUXER (MM, mm); + REGISTER_MUXDEMUX(MMF, mmf); + REGISTER_MUXDEMUX(MOV, mov); + REGISTER_MUXER (MP2, mp2); + REGISTER_MUXDEMUX(MP3, mp3); + REGISTER_MUXER (MP4, mp4); + REGISTER_DEMUXER (MPC, mpc); + REGISTER_DEMUXER (MPC8, mpc8); + REGISTER_MUXER (MPEG1SYSTEM, mpeg1system); + REGISTER_MUXER (MPEG1VCD, mpeg1vcd); + REGISTER_MUXER (MPEG1VIDEO, mpeg1video); + REGISTER_MUXER (MPEG2DVD, mpeg2dvd); + REGISTER_MUXER (MPEG2SVCD, mpeg2svcd); + REGISTER_MUXER (MPEG2VIDEO, mpeg2video); + REGISTER_MUXER (MPEG2VOB, mpeg2vob); + REGISTER_DEMUXER (MPEGPS, mpegps); + REGISTER_MUXDEMUX(MPEGTS, mpegts); + REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw); + REGISTER_DEMUXER (MPEGVIDEO, mpegvideo); + REGISTER_MUXER (MPJPEG, mpjpeg); + REGISTER_DEMUXER (MPL2, mpl2); + REGISTER_DEMUXER (MPSUB, mpsub); + REGISTER_DEMUXER (MSNWC_TCP, msnwc_tcp); + REGISTER_DEMUXER (MTV, mtv); + REGISTER_DEMUXER (MV, mv); + REGISTER_DEMUXER (MVI, mvi); + REGISTER_MUXDEMUX(MXF, mxf); + REGISTER_MUXER (MXF_D10, mxf_d10); + REGISTER_DEMUXER (MXG, mxg); + REGISTER_DEMUXER (NC, nc); + REGISTER_DEMUXER (NISTSPHERE, nistsphere); + REGISTER_DEMUXER (NSV, nsv); + REGISTER_MUXER (NULL, null); + REGISTER_MUXDEMUX(NUT, nut); + REGISTER_DEMUXER (NUV, nuv); + REGISTER_MUXDEMUX(OGG, ogg); + REGISTER_MUXDEMUX(OMA, oma); + REGISTER_DEMUXER (PAF, paf); + REGISTER_MUXDEMUX(PCM_ALAW, pcm_alaw); + REGISTER_MUXDEMUX(PCM_MULAW, pcm_mulaw); + REGISTER_MUXDEMUX(PCM_F64BE, pcm_f64be); + REGISTER_MUXDEMUX(PCM_F64LE, pcm_f64le); + REGISTER_MUXDEMUX(PCM_F32BE, pcm_f32be); + REGISTER_MUXDEMUX(PCM_F32LE, pcm_f32le); + REGISTER_MUXDEMUX(PCM_S32BE, pcm_s32be); + REGISTER_MUXDEMUX(PCM_S32LE, pcm_s32le); + REGISTER_MUXDEMUX(PCM_S24BE, pcm_s24be); + REGISTER_MUXDEMUX(PCM_S24LE, pcm_s24le); + REGISTER_MUXDEMUX(PCM_S16BE, pcm_s16be); + REGISTER_MUXDEMUX(PCM_S16LE, pcm_s16le); + REGISTER_MUXDEMUX(PCM_S8, pcm_s8); + REGISTER_MUXDEMUX(PCM_U32BE, pcm_u32be); + REGISTER_MUXDEMUX(PCM_U32LE, pcm_u32le); + REGISTER_MUXDEMUX(PCM_U24BE, pcm_u24be); + REGISTER_MUXDEMUX(PCM_U24LE, pcm_u24le); + REGISTER_MUXDEMUX(PCM_U16BE, pcm_u16be); + REGISTER_MUXDEMUX(PCM_U16LE, pcm_u16le); + REGISTER_MUXDEMUX(PCM_U8, pcm_u8); + REGISTER_DEMUXER (PJS, pjs); + REGISTER_DEMUXER (PMP, pmp); + REGISTER_MUXER (PSP, psp); + REGISTER_DEMUXER (PVA, pva); + REGISTER_DEMUXER (PVF, pvf); + REGISTER_DEMUXER (QCP, qcp); + REGISTER_DEMUXER (R3D, r3d); + REGISTER_MUXDEMUX(RAWVIDEO, rawvideo); + REGISTER_DEMUXER (REALTEXT, realtext); + REGISTER_DEMUXER (RL2, rl2); + REGISTER_MUXDEMUX(RM, rm); + REGISTER_MUXDEMUX(ROQ, roq); + REGISTER_DEMUXER (RPL, rpl); + REGISTER_MUXDEMUX(RSO, rso); + REGISTER_MUXDEMUX(RTP, rtp); + REGISTER_MUXDEMUX(RTSP, rtsp); + REGISTER_DEMUXER (SAMI, sami); + REGISTER_MUXDEMUX(SAP, sap); + REGISTER_DEMUXER (SBG, sbg); + REGISTER_DEMUXER (SDP, sdp); +#if CONFIG_RTPDEC + av_register_rtp_dynamic_payload_handlers(); + av_register_rdt_dynamic_payload_handlers(); +#endif + REGISTER_DEMUXER (SEGAFILM, segafilm); + REGISTER_MUXER (SEGMENT, segment); + REGISTER_MUXER (SEGMENT, stream_segment); + REGISTER_DEMUXER (SHORTEN, shorten); + REGISTER_DEMUXER (SIFF, siff); + REGISTER_DEMUXER (SMACKER, smacker); + REGISTER_MUXDEMUX(SMJPEG, smjpeg); + REGISTER_MUXER (SMOOTHSTREAMING, smoothstreaming); + REGISTER_DEMUXER (SMUSH, smush); + REGISTER_DEMUXER (SOL, sol); + REGISTER_MUXDEMUX(SOX, sox); + REGISTER_MUXDEMUX(SPDIF, spdif); + REGISTER_MUXDEMUX(SRT, srt); + REGISTER_DEMUXER (STR, str); + REGISTER_DEMUXER (SUBVIEWER1, subviewer1); + REGISTER_DEMUXER (SUBVIEWER, subviewer); + REGISTER_MUXDEMUX(SWF, swf); + REGISTER_DEMUXER (TAK, tak); + REGISTER_MUXER (TEE, tee); + REGISTER_DEMUXER (TEDCAPTIONS, tedcaptions); + REGISTER_MUXER (TG2, tg2); + REGISTER_MUXER (TGP, tgp); + REGISTER_DEMUXER (THP, thp); + REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq); + REGISTER_MUXER (MKVTIMESTAMP_V2, mkvtimestamp_v2); + REGISTER_DEMUXER (TMV, tmv); + REGISTER_MUXDEMUX(TRUEHD, truehd); + REGISTER_DEMUXER (TTA, tta); + REGISTER_DEMUXER (TXD, txd); + REGISTER_DEMUXER (TTY, tty); + REGISTER_MUXDEMUX(VC1, vc1); + REGISTER_MUXDEMUX(VC1T, vc1t); + REGISTER_DEMUXER (VIVO, vivo); + REGISTER_DEMUXER (VMD, vmd); + REGISTER_DEMUXER (VOBSUB, vobsub); + REGISTER_MUXDEMUX(VOC, voc); + REGISTER_DEMUXER (VPLAYER, vplayer); + REGISTER_DEMUXER (VQF, vqf); + REGISTER_MUXDEMUX(W64, w64); + REGISTER_MUXDEMUX(WAV, wav); + REGISTER_DEMUXER (WC3, wc3); + REGISTER_MUXER (WEBM, webm); + REGISTER_DEMUXER (WEBVTT, webvtt); + REGISTER_DEMUXER (WSAUD, wsaud); + REGISTER_DEMUXER (WSVQA, wsvqa); + REGISTER_MUXDEMUX(WTV, wtv); + REGISTER_MUXDEMUX(WV, wv); + REGISTER_DEMUXER (XA, xa); + REGISTER_DEMUXER (XBIN, xbin); + REGISTER_DEMUXER (XMV, xmv); + REGISTER_DEMUXER (XWMA, xwma); + REGISTER_DEMUXER (YOP, yop); + REGISTER_MUXDEMUX(YUV4MPEGPIPE, yuv4mpegpipe); + + /* protocols */ + REGISTER_PROTOCOL(BLURAY, bluray); + REGISTER_PROTOCOL(CACHE, cache); + REGISTER_PROTOCOL(CONCAT, concat); + REGISTER_PROTOCOL(CRYPTO, crypto); + REGISTER_PROTOCOL(DATA, data); + REGISTER_PROTOCOL(FFRTMPCRYPT, ffrtmpcrypt); + REGISTER_PROTOCOL(FFRTMPHTTP, ffrtmphttp); + REGISTER_PROTOCOL(FILE, file); + REGISTER_PROTOCOL(GOPHER, gopher); + REGISTER_PROTOCOL(HLS, hls); + REGISTER_PROTOCOL(HTTP, http); + REGISTER_PROTOCOL(HTTPPROXY, httpproxy); + REGISTER_PROTOCOL(HTTPS, https); + REGISTER_PROTOCOL(MMSH, mmsh); + REGISTER_PROTOCOL(MMST, mmst); + REGISTER_PROTOCOL(MD5, md5); + REGISTER_PROTOCOL(PIPE, pipe); + REGISTER_PROTOCOL(RTMP, rtmp); + REGISTER_PROTOCOL(RTMPE, rtmpe); + REGISTER_PROTOCOL(RTMPS, rtmps); + REGISTER_PROTOCOL(RTMPT, rtmpt); + REGISTER_PROTOCOL(RTMPTE, rtmpte); + REGISTER_PROTOCOL(RTMPTS, rtmpts); + REGISTER_PROTOCOL(RTP, rtp); + REGISTER_PROTOCOL(SCTP, sctp); + REGISTER_PROTOCOL(SRTP, srtp); + REGISTER_PROTOCOL(TCP, tcp); + REGISTER_PROTOCOL(TLS, tls); + REGISTER_PROTOCOL(UDP, udp); + + /* external libraries */ + REGISTER_DEMUXER (LIBMODPLUG, libmodplug); + REGISTER_MUXDEMUX(LIBNUT, libnut); + REGISTER_DEMUXER (LIBQUVI, libquvi); + REGISTER_PROTOCOL(LIBRTMP, librtmp); + REGISTER_PROTOCOL(LIBRTMPE, librtmpe); + REGISTER_PROTOCOL(LIBRTMPS, librtmps); + REGISTER_PROTOCOL(LIBRTMPT, librtmpt); + REGISTER_PROTOCOL(LIBRTMPTE, librtmpte); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/allformats.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/allformats.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/allformats.o libavformat/allformats.o: \ + libavformat/allformats.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rtp.h \ + libavformat/avformat.h libavformat/rdt.h libavformat/rtpdec.h \ + libavformat/url.h libavformat/srtp.h libavformat/version.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/allformats.o Binary file ffmpeg/libavformat/allformats.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/amr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/amr.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,181 @@ +/* + * amr file format + * Copyright (c) 2001 ffmpeg project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* +Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.txt?number=3267 + +Only mono files are supported. + +*/ + +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +static const char AMR_header[] = "#!AMR\n"; +static const char AMRWB_header[] = "#!AMR-WB\n"; + +#if CONFIG_AMR_MUXER +static int amr_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + s->priv_data = NULL; + + if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + avio_write(pb, AMR_header, sizeof(AMR_header) - 1); /* magic number */ + } else if (enc->codec_id == AV_CODEC_ID_AMR_WB) { + avio_write(pb, AMRWB_header, sizeof(AMRWB_header) - 1); /* magic number */ + } else { + return -1; + } + avio_flush(pb); + return 0; +} + +static int amr_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} +#endif /* CONFIG_AMR_MUXER */ + +static int amr_probe(AVProbeData *p) +{ + // Only check for "#!AMR" which could be amr-wb, amr-nb. + // This will also trigger multichannel files: "#!AMR_MC1.0\n" and + // "#!AMR-WB_MC1.0\n" (not supported) + + if (!memcmp(p->buf, AMR_header, 5)) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +/* amr input */ +static int amr_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + uint8_t header[9]; + + avio_read(pb, header, 6); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + if (memcmp(header, AMR_header, 6)) { + avio_read(pb, header + 6, 3); + if (memcmp(header, AMRWB_header, 9)) { + return -1; + } + + st->codec->codec_tag = MKTAG('s', 'a', 'w', 'b'); + st->codec->codec_id = AV_CODEC_ID_AMR_WB; + st->codec->sample_rate = 16000; + } else { + st->codec->codec_tag = MKTAG('s', 'a', 'm', 'r'); + st->codec->codec_id = AV_CODEC_ID_AMR_NB; + st->codec->sample_rate = 8000; + } + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecContext *enc = s->streams[0]->codec; + int read, size = 0, toc, mode; + int64_t pos = avio_tell(s->pb); + + if (url_feof(s->pb)) { + return AVERROR(EIO); + } + + // FIXME this is wrong, this should rather be in a AVParset + toc = avio_r8(s->pb); + mode = (toc >> 3) & 0x0F; + + if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + static const uint8_t packed_size[16] = { + 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 + }; + + size = packed_size[mode] + 1; + } else if (enc->codec_id == AV_CODEC_ID_AMR_WB) { + static const uint8_t packed_size[16] = { + 18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 6, 0, 0, 0, 1, 1 + }; + + size = packed_size[mode]; + } else { + av_assert0(0); + } + + if (!size || av_new_packet(pkt, size)) + return AVERROR(EIO); + + /* Both AMR formats have 50 frames per second */ + s->streams[0]->codec->bit_rate = size*8*50; + + pkt->stream_index = 0; + pkt->pos = pos; + pkt->data[0] = toc; + pkt->duration = enc->codec_id == AV_CODEC_ID_AMR_NB ? 160 : 320; + read = avio_read(s->pb, pkt->data + 1, size - 1); + + if (read != size - 1) { + av_free_packet(pkt); + return AVERROR(EIO); + } + + return 0; +} + +#if CONFIG_AMR_DEMUXER +AVInputFormat ff_amr_demuxer = { + .name = "amr", + .long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), + .read_probe = amr_probe, + .read_header = amr_read_header, + .read_packet = amr_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; +#endif + +#if CONFIG_AMR_MUXER +AVOutputFormat ff_amr_muxer = { + .name = "amr", + .long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), + .mime_type = "audio/amr", + .extensions = "amr", + .audio_codec = AV_CODEC_ID_AMR_NB, + .video_codec = AV_CODEC_ID_NONE, + .write_header = amr_write_header, + .write_packet = amr_write_packet, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/amr.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/amr.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/amr.o libavformat/amr.o: libavformat/amr.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/channel_layout.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/amr.o Binary file ffmpeg/libavformat/amr.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/anm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/anm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,229 @@ +/* + * Deluxe Paint Animation demuxer + * Copyright (c) 2009 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Deluxe Paint Animation demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int base_record; + unsigned int nb_records; + int size; +} Page; + +typedef struct { + unsigned int nb_pages; /**< total pages in file */ + unsigned int nb_records; /**< total records in file */ + int page_table_offset; +#define MAX_PAGES 256 /**< Deluxe Paint hardcoded value */ + Page pt[MAX_PAGES]; /**< page table */ + int page; /**< current page (or AVERROR_xxx code) */ + int record; /**< current record (with in page) */ +} AnmDemuxContext; + +#define LPF_TAG MKTAG('L','P','F',' ') +#define ANIM_TAG MKTAG('A','N','I','M') + +static int probe(AVProbeData *p) +{ + /* verify tags and video dimensions */ + if (AV_RL32(&p->buf[0]) == LPF_TAG && + AV_RL32(&p->buf[16]) == ANIM_TAG && + AV_RL16(&p->buf[20]) && AV_RL16(&p->buf[22])) + return AVPROBE_SCORE_MAX; + return 0; +} + +/** + * @return page containing the requested record or AVERROR_XXX + */ +static int find_record(const AnmDemuxContext *anm, int record) +{ + int i; + + if (record >= anm->nb_records) + return AVERROR_EOF; + + for (i = 0; i < MAX_PAGES; i++) { + const Page *p = &anm->pt[i]; + if (p->nb_records > 0 && record >= p->base_record && record < p->base_record + p->nb_records) + return i; + } + + return AVERROR_INVALIDDATA; +} + +static int read_header(AVFormatContext *s) +{ + AnmDemuxContext *anm = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int i, ret; + + avio_skip(pb, 4); /* magic number */ + if (avio_rl16(pb) != MAX_PAGES) { + avpriv_request_sample(s, "max_pages != " AV_STRINGIFY(MAX_PAGES)); + return AVERROR_PATCHWELCOME; + } + + anm->nb_pages = avio_rl16(pb); + anm->nb_records = avio_rl32(pb); + avio_skip(pb, 2); /* max records per page */ + anm->page_table_offset = avio_rl16(pb); + if (avio_rl32(pb) != ANIM_TAG) + return AVERROR_INVALIDDATA; + + /* video stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_ANM; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = avio_rl16(pb); + st->codec->height = avio_rl16(pb); + if (avio_r8(pb) != 0) + goto invalid; + avio_skip(pb, 1); /* frame rate multiplier info */ + + /* ignore last delta record (used for looping) */ + if (avio_r8(pb)) /* has_last_delta */ + anm->nb_records = FFMAX(anm->nb_records - 1, 0); + + avio_skip(pb, 1); /* last_delta_valid */ + + if (avio_r8(pb) != 0) + goto invalid; + + if (avio_r8(pb) != 1) + goto invalid; + + avio_skip(pb, 1); /* other recs per frame */ + + if (avio_r8(pb) != 1) + goto invalid; + + avio_skip(pb, 32); /* record_types */ + st->nb_frames = avio_rl32(pb); + avpriv_set_pts_info(st, 64, 1, avio_rl16(pb)); + avio_skip(pb, 58); + + /* color cycling and palette data */ + st->codec->extradata_size = 16*8 + 4*256; + st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) { + return AVERROR(ENOMEM); + } + ret = avio_read(pb, st->codec->extradata, st->codec->extradata_size); + if (ret < 0) + return ret; + + /* read page table */ + ret = avio_seek(pb, anm->page_table_offset, SEEK_SET); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_PAGES; i++) { + Page *p = &anm->pt[i]; + p->base_record = avio_rl16(pb); + p->nb_records = avio_rl16(pb); + p->size = avio_rl16(pb); + } + + /* find page of first frame */ + anm->page = find_record(anm, 0); + if (anm->page < 0) { + return anm->page; + } + + anm->record = -1; + return 0; + +invalid: + avpriv_request_sample(s, "Invalid header element"); + return AVERROR_PATCHWELCOME; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AnmDemuxContext *anm = s->priv_data; + AVIOContext *pb = s->pb; + Page *p; + int tmp, record_size; + + if (url_feof(s->pb)) + return AVERROR(EIO); + + if (anm->page < 0) + return anm->page; + +repeat: + p = &anm->pt[anm->page]; + + /* parse page header */ + if (anm->record < 0) { + avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16), SEEK_SET); + avio_skip(pb, 8 + 2*p->nb_records); + anm->record = 0; + } + + /* if we have fetched all records in this page, then find the + next page and repeat */ + if (anm->record >= p->nb_records) { + anm->page = find_record(anm, p->base_record + p->nb_records); + if (anm->page < 0) + return anm->page; + anm->record = -1; + goto repeat; + } + + /* fetch record size */ + tmp = avio_tell(pb); + avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16) + + 8 + anm->record * 2, SEEK_SET); + record_size = avio_rl16(pb); + avio_seek(pb, tmp, SEEK_SET); + + /* fetch record */ + pkt->size = av_get_packet(s->pb, pkt, record_size); + if (pkt->size < 0) + return pkt->size; + if (p->base_record + anm->record == 0) + pkt->flags |= AV_PKT_FLAG_KEY; + + anm->record++; + return 0; +} + +AVInputFormat ff_anm_demuxer = { + .name = "anm", + .long_name = NULL_IF_CONFIG_SMALL("Deluxe Paint Animation"), + .priv_data_size = sizeof(AnmDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/anm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/anm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/anm.o libavformat/anm.o: libavformat/anm.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/anm.o Binary file ffmpeg/libavformat/anm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/apc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,96 @@ +/* + * CRYO APC audio format demuxer + * Copyright (c) 2007 Anssi Hannula + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/channel_layout.h" +#include "avformat.h" + +static int apc_probe(AVProbeData *p) +{ + if (!strncmp(p->buf, "CRYO_APC", 8)) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int apc_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + + avio_rl32(pb); /* CRYO */ + avio_rl32(pb); /* _APC */ + avio_rl32(pb); /* 1.20 */ + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_APC; + + avio_rl32(pb); /* number of samples */ + st->codec->sample_rate = avio_rl32(pb); + + st->codec->extradata_size = 2 * 4; + st->codec->extradata = av_malloc(st->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + + /* initial predictor values for adpcm decoder */ + avio_read(pb, st->codec->extradata, 2 * 4); + + if (avio_rl32(pb)) { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + + st->codec->bits_per_coded_sample = 4; + st->codec->bit_rate = st->codec->bits_per_coded_sample * st->codec->channels + * st->codec->sample_rate; + st->codec->block_align = 1; + + return 0; +} + +#define MAX_READ_SIZE 4096 + +static int apc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + if (av_get_packet(s->pb, pkt, MAX_READ_SIZE) <= 0) + return AVERROR(EIO); + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + return 0; +} + +AVInputFormat ff_apc_demuxer = { + .name = "apc", + .long_name = NULL_IF_CONFIG_SMALL("CRYO APC"), + .read_probe = apc_probe, + .read_header = apc_read_header, + .read_packet = apc_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/apc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/apc.o libavformat/apc.o: libavformat/apc.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apc.o Binary file ffmpeg/libavformat/apc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ape.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ape.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,465 @@ +/* + * Monkey's Audio APE demuxer + * Copyright (c) 2007 Benjamin Zores + * based upon libdemac from Dave Chapman. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "apetag.h" + +/* The earliest and latest file formats supported by this library */ +#define APE_MIN_VERSION 3800 +#define APE_MAX_VERSION 3990 + +#define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE] +#define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE] +#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE] +#define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE] +#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level +#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) + +#define APE_EXTRADATA_SIZE 6 + +typedef struct { + int64_t pos; + int nblocks; + int size; + int skip; + int64_t pts; +} APEFrame; + +typedef struct { + /* Derived fields */ + uint32_t junklength; + uint32_t firstframe; + uint32_t totalsamples; + int currentframe; + APEFrame *frames; + + /* Info from Descriptor Block */ + char magic[4]; + int16_t fileversion; + int16_t padding1; + uint32_t descriptorlength; + uint32_t headerlength; + uint32_t seektablelength; + uint32_t wavheaderlength; + uint32_t audiodatalength; + uint32_t audiodatalength_high; + uint32_t wavtaillength; + uint8_t md5[16]; + + /* Info from Header Block */ + uint16_t compressiontype; + uint16_t formatflags; + uint32_t blocksperframe; + uint32_t finalframeblocks; + uint32_t totalframes; + uint16_t bps; + uint16_t channels; + uint32_t samplerate; + + /* Seektable */ + uint32_t *seektable; + uint8_t *bittable; +} APEContext; + +static int ape_probe(AVProbeData * p) +{ + if (p->buf[0] == 'M' && p->buf[1] == 'A' && p->buf[2] == 'C' && p->buf[3] == ' ') + return AVPROBE_SCORE_MAX; + + return 0; +} + +static void ape_dumpinfo(AVFormatContext * s, APEContext * ape_ctx) +{ +#ifdef DEBUG + int i; + + av_log(s, AV_LOG_DEBUG, "Descriptor Block:\n\n"); + av_log(s, AV_LOG_DEBUG, "magic = \"%c%c%c%c\"\n", ape_ctx->magic[0], ape_ctx->magic[1], ape_ctx->magic[2], ape_ctx->magic[3]); + av_log(s, AV_LOG_DEBUG, "fileversion = %"PRId16"\n", ape_ctx->fileversion); + av_log(s, AV_LOG_DEBUG, "descriptorlength = %"PRIu32"\n", ape_ctx->descriptorlength); + av_log(s, AV_LOG_DEBUG, "headerlength = %"PRIu32"\n", ape_ctx->headerlength); + av_log(s, AV_LOG_DEBUG, "seektablelength = %"PRIu32"\n", ape_ctx->seektablelength); + av_log(s, AV_LOG_DEBUG, "wavheaderlength = %"PRIu32"\n", ape_ctx->wavheaderlength); + av_log(s, AV_LOG_DEBUG, "audiodatalength = %"PRIu32"\n", ape_ctx->audiodatalength); + av_log(s, AV_LOG_DEBUG, "audiodatalength_high = %"PRIu32"\n", ape_ctx->audiodatalength_high); + av_log(s, AV_LOG_DEBUG, "wavtaillength = %"PRIu32"\n", ape_ctx->wavtaillength); + av_log(s, AV_LOG_DEBUG, "md5 = "); + for (i = 0; i < 16; i++) + av_log(s, AV_LOG_DEBUG, "%02x", ape_ctx->md5[i]); + av_log(s, AV_LOG_DEBUG, "\n"); + + av_log(s, AV_LOG_DEBUG, "\nHeader Block:\n\n"); + + av_log(s, AV_LOG_DEBUG, "compressiontype = %"PRIu16"\n", ape_ctx->compressiontype); + av_log(s, AV_LOG_DEBUG, "formatflags = %"PRIu16"\n", ape_ctx->formatflags); + av_log(s, AV_LOG_DEBUG, "blocksperframe = %"PRIu32"\n", ape_ctx->blocksperframe); + av_log(s, AV_LOG_DEBUG, "finalframeblocks = %"PRIu32"\n", ape_ctx->finalframeblocks); + av_log(s, AV_LOG_DEBUG, "totalframes = %"PRIu32"\n", ape_ctx->totalframes); + av_log(s, AV_LOG_DEBUG, "bps = %"PRIu16"\n", ape_ctx->bps); + av_log(s, AV_LOG_DEBUG, "channels = %"PRIu16"\n", ape_ctx->channels); + av_log(s, AV_LOG_DEBUG, "samplerate = %"PRIu32"\n", ape_ctx->samplerate); + + av_log(s, AV_LOG_DEBUG, "\nSeektable\n\n"); + if ((ape_ctx->seektablelength / sizeof(uint32_t)) != ape_ctx->totalframes) { + av_log(s, AV_LOG_DEBUG, "No seektable\n"); + } else { + for (i = 0; i < ape_ctx->seektablelength / sizeof(uint32_t); i++) { + if (i < ape_ctx->totalframes - 1) { + av_log(s, AV_LOG_DEBUG, "%8d %"PRIu32" (%"PRIu32" bytes)", + i, ape_ctx->seektable[i], + ape_ctx->seektable[i + 1] - ape_ctx->seektable[i]); + if (ape_ctx->bittable) + av_log(s, AV_LOG_DEBUG, " + %2d bits\n", + ape_ctx->bittable[i]); + av_log(s, AV_LOG_DEBUG, "\n"); + } else { + av_log(s, AV_LOG_DEBUG, "%8d %"PRIu32"\n", i, ape_ctx->seektable[i]); + } + } + } + + av_log(s, AV_LOG_DEBUG, "\nFrames\n\n"); + for (i = 0; i < ape_ctx->totalframes; i++) + av_log(s, AV_LOG_DEBUG, "%8d %8"PRId64" %8d (%d samples)\n", i, + ape_ctx->frames[i].pos, ape_ctx->frames[i].size, + ape_ctx->frames[i].nblocks); + + av_log(s, AV_LOG_DEBUG, "\nCalculated information:\n\n"); + av_log(s, AV_LOG_DEBUG, "junklength = %"PRIu32"\n", ape_ctx->junklength); + av_log(s, AV_LOG_DEBUG, "firstframe = %"PRIu32"\n", ape_ctx->firstframe); + av_log(s, AV_LOG_DEBUG, "totalsamples = %"PRIu32"\n", ape_ctx->totalsamples); +#endif +} + +static int ape_read_header(AVFormatContext * s) +{ + AVIOContext *pb = s->pb; + APEContext *ape = s->priv_data; + AVStream *st; + uint32_t tag; + int i; + int total_blocks, final_size = 0; + int64_t pts, file_size; + + /* Skip any leading junk such as id3v2 tags */ + ape->junklength = avio_tell(pb); + + tag = avio_rl32(pb); + if (tag != MKTAG('M', 'A', 'C', ' ')) + return AVERROR_INVALIDDATA; + + ape->fileversion = avio_rl16(pb); + + if (ape->fileversion < APE_MIN_VERSION || ape->fileversion > APE_MAX_VERSION) { + av_log(s, AV_LOG_ERROR, "Unsupported file version - %d.%02d\n", + ape->fileversion / 1000, (ape->fileversion % 1000) / 10); + return AVERROR_PATCHWELCOME; + } + + if (ape->fileversion >= 3980) { + ape->padding1 = avio_rl16(pb); + ape->descriptorlength = avio_rl32(pb); + ape->headerlength = avio_rl32(pb); + ape->seektablelength = avio_rl32(pb); + ape->wavheaderlength = avio_rl32(pb); + ape->audiodatalength = avio_rl32(pb); + ape->audiodatalength_high = avio_rl32(pb); + ape->wavtaillength = avio_rl32(pb); + avio_read(pb, ape->md5, 16); + + /* Skip any unknown bytes at the end of the descriptor. + This is for future compatibility */ + if (ape->descriptorlength > 52) + avio_skip(pb, ape->descriptorlength - 52); + + /* Read header data */ + ape->compressiontype = avio_rl16(pb); + ape->formatflags = avio_rl16(pb); + ape->blocksperframe = avio_rl32(pb); + ape->finalframeblocks = avio_rl32(pb); + ape->totalframes = avio_rl32(pb); + ape->bps = avio_rl16(pb); + ape->channels = avio_rl16(pb); + ape->samplerate = avio_rl32(pb); + } else { + ape->descriptorlength = 0; + ape->headerlength = 32; + + ape->compressiontype = avio_rl16(pb); + ape->formatflags = avio_rl16(pb); + ape->channels = avio_rl16(pb); + ape->samplerate = avio_rl32(pb); + ape->wavheaderlength = avio_rl32(pb); + ape->wavtaillength = avio_rl32(pb); + ape->totalframes = avio_rl32(pb); + ape->finalframeblocks = avio_rl32(pb); + + if (ape->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) { + avio_skip(pb, 4); /* Skip the peak level */ + ape->headerlength += 4; + } + + if (ape->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) { + ape->seektablelength = avio_rl32(pb); + ape->headerlength += 4; + ape->seektablelength *= sizeof(int32_t); + } else + ape->seektablelength = ape->totalframes * sizeof(int32_t); + + if (ape->formatflags & MAC_FORMAT_FLAG_8_BIT) + ape->bps = 8; + else if (ape->formatflags & MAC_FORMAT_FLAG_24_BIT) + ape->bps = 24; + else + ape->bps = 16; + + if (ape->fileversion >= 3950) + ape->blocksperframe = 73728 * 4; + else if (ape->fileversion >= 3900 || (ape->fileversion >= 3800 && ape->compressiontype >= 4000)) + ape->blocksperframe = 73728; + else + ape->blocksperframe = 9216; + + /* Skip any stored wav header */ + if (!(ape->formatflags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)) + avio_skip(pb, ape->wavheaderlength); + } + + if(!ape->totalframes){ + av_log(s, AV_LOG_ERROR, "No frames in the file!\n"); + return AVERROR(EINVAL); + } + if(ape->totalframes > UINT_MAX / sizeof(APEFrame)){ + av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", + ape->totalframes); + return AVERROR_INVALIDDATA; + } + if (ape->seektablelength && (ape->seektablelength / sizeof(*ape->seektable)) < ape->totalframes) { + av_log(s, AV_LOG_ERROR, + "Number of seek entries is less than number of frames: %zu vs. %"PRIu32"\n", + ape->seektablelength / sizeof(*ape->seektable), ape->totalframes); + return AVERROR_INVALIDDATA; + } + ape->frames = av_malloc(ape->totalframes * sizeof(APEFrame)); + if(!ape->frames) + return AVERROR(ENOMEM); + ape->firstframe = ape->junklength + ape->descriptorlength + ape->headerlength + ape->seektablelength + ape->wavheaderlength; + if (ape->fileversion < 3810) + ape->firstframe += ape->totalframes; + ape->currentframe = 0; + + + ape->totalsamples = ape->finalframeblocks; + if (ape->totalframes > 1) + ape->totalsamples += ape->blocksperframe * (ape->totalframes - 1); + + if (ape->seektablelength > 0) { + ape->seektable = av_malloc(ape->seektablelength); + if (!ape->seektable) + return AVERROR(ENOMEM); + for (i = 0; i < ape->seektablelength / sizeof(uint32_t); i++) + ape->seektable[i] = avio_rl32(pb); + if (ape->fileversion < 3810) { + ape->bittable = av_malloc(ape->totalframes); + if (!ape->bittable) + return AVERROR(ENOMEM); + for (i = 0; i < ape->totalframes; i++) + ape->bittable[i] = avio_r8(pb); + } + }else{ + av_log(s, AV_LOG_ERROR, "Missing seektable\n"); + return AVERROR_INVALIDDATA; + } + + ape->frames[0].pos = ape->firstframe; + ape->frames[0].nblocks = ape->blocksperframe; + ape->frames[0].skip = 0; + for (i = 1; i < ape->totalframes; i++) { + ape->frames[i].pos = ape->seektable[i] + ape->junklength; + ape->frames[i].nblocks = ape->blocksperframe; + ape->frames[i - 1].size = ape->frames[i].pos - ape->frames[i - 1].pos; + ape->frames[i].skip = (ape->frames[i].pos - ape->frames[0].pos) & 3; + } + ape->frames[ape->totalframes - 1].nblocks = ape->finalframeblocks; + /* calculate final packet size from total file size, if available */ + file_size = avio_size(pb); + if (file_size > 0) { + final_size = file_size - ape->frames[ape->totalframes - 1].pos - + ape->wavtaillength; + final_size -= final_size & 3; + } + if (file_size <= 0 || final_size <= 0) + final_size = ape->finalframeblocks * 8; + ape->frames[ape->totalframes - 1].size = final_size; + + for (i = 0; i < ape->totalframes; i++) { + if(ape->frames[i].skip){ + ape->frames[i].pos -= ape->frames[i].skip; + ape->frames[i].size += ape->frames[i].skip; + } + ape->frames[i].size = (ape->frames[i].size + 3) & ~3; + } + if (ape->fileversion < 3810) { + for (i = 0; i < ape->totalframes; i++) { + if (i < ape->totalframes - 1 && ape->bittable[i + 1]) + ape->frames[i].size += 4; + ape->frames[i].skip <<= 3; + ape->frames[i].skip += ape->bittable[i]; + } + } + + ape_dumpinfo(s, ape); + + av_log(s, AV_LOG_DEBUG, "Decoding file - v%d.%02d, compression level %"PRIu16"\n", + ape->fileversion / 1000, (ape->fileversion % 1000) / 10, + ape->compressiontype); + + /* now we are ready: build format streams */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_APE; + st->codec->codec_tag = MKTAG('A', 'P', 'E', ' '); + st->codec->channels = ape->channels; + st->codec->sample_rate = ape->samplerate; + st->codec->bits_per_coded_sample = ape->bps; + + st->nb_frames = ape->totalframes; + st->start_time = 0; + st->duration = total_blocks; + avpriv_set_pts_info(st, 64, 1, ape->samplerate); + + st->codec->extradata = av_malloc(APE_EXTRADATA_SIZE); + st->codec->extradata_size = APE_EXTRADATA_SIZE; + AV_WL16(st->codec->extradata + 0, ape->fileversion); + AV_WL16(st->codec->extradata + 2, ape->compressiontype); + AV_WL16(st->codec->extradata + 4, ape->formatflags); + + pts = 0; + for (i = 0; i < ape->totalframes; i++) { + ape->frames[i].pts = pts; + av_add_index_entry(st, ape->frames[i].pos, ape->frames[i].pts, 0, 0, AVINDEX_KEYFRAME); + pts += ape->blocksperframe; + } + + /* try to read APE tags */ + if (pb->seekable) { + ff_ape_parse_tag(s); + avio_seek(pb, 0, SEEK_SET); + } + + return 0; +} + +static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) +{ + int ret; + int nblocks; + APEContext *ape = s->priv_data; + uint32_t extra_size = 8; + + if (url_feof(s->pb)) + return AVERROR_EOF; + if (ape->currentframe >= ape->totalframes) + return AVERROR_EOF; + + if (avio_seek(s->pb, ape->frames[ape->currentframe].pos, SEEK_SET) < 0) + return AVERROR(EIO); + + /* Calculate how many blocks there are in this frame */ + if (ape->currentframe == (ape->totalframes - 1)) + nblocks = ape->finalframeblocks; + else + nblocks = ape->blocksperframe; + + if (ape->frames[ape->currentframe].size <= 0 || + ape->frames[ape->currentframe].size > INT_MAX - extra_size) { + av_log(s, AV_LOG_ERROR, "invalid packet size: %d\n", + ape->frames[ape->currentframe].size); + ape->currentframe++; + return AVERROR(EIO); + } + + if (av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size) < 0) + return AVERROR(ENOMEM); + + AV_WL32(pkt->data , nblocks); + AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip); + ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size); + + pkt->pts = ape->frames[ape->currentframe].pts; + pkt->stream_index = 0; + + /* note: we need to modify the packet size here to handle the last + packet */ + pkt->size = ret + extra_size; + + ape->currentframe++; + + return 0; +} + +static int ape_read_close(AVFormatContext * s) +{ + APEContext *ape = s->priv_data; + + av_freep(&ape->frames); + av_freep(&ape->seektable); + av_freep(&ape->bittable); + return 0; +} + +static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVStream *st = s->streams[stream_index]; + APEContext *ape = s->priv_data; + int index = av_index_search_timestamp(st, timestamp, flags); + + if (index < 0) + return -1; + + if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) + return -1; + ape->currentframe = index; + return 0; +} + +AVInputFormat ff_ape_demuxer = { + .name = "ape", + .long_name = NULL_IF_CONFIG_SMALL("Monkey's Audio"), + .priv_data_size = sizeof(APEContext), + .read_probe = ape_probe, + .read_header = ape_read_header, + .read_packet = ape_read_packet, + .read_close = ape_read_close, + .read_seek = ape_read_seek, + .extensions = "ape,apl,mac", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ape.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ape.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/ape.o libavformat/ape.o: libavformat/ape.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/apetag.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ape.o Binary file ffmpeg/libavformat/ape.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apetag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/apetag.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,169 @@ +/* + * APE tag handling + * Copyright (c) 2007 Benjamin Zores + * based upon libdemac from Dave Chapman. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "avformat.h" +#include "apetag.h" +#include "internal.h" + +#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) +#define APE_TAG_FLAG_IS_HEADER (1 << 29) +#define APE_TAG_FLAG_IS_BINARY (1 << 1) + +static int ape_tag_read_field(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + uint8_t key[1024], *value; + uint32_t size, flags; + int i, c; + + size = avio_rl32(pb); /* field size */ + flags = avio_rl32(pb); /* field flags */ + for (i = 0; i < sizeof(key) - 1; i++) { + c = avio_r8(pb); + if (c < 0x20 || c > 0x7E) + break; + else + key[i] = c; + } + key[i] = 0; + if (c != 0) { + av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key); + return -1; + } + if (size >= UINT_MAX) + return -1; + if (flags & APE_TAG_FLAG_IS_BINARY) { + uint8_t filename[1024]; + enum AVCodecID id; + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + size -= avio_get_str(pb, size, filename, sizeof(filename)); + if (size <= 0) { + av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key); + return 0; + } + + av_dict_set(&st->metadata, key, filename, 0); + + if ((id = ff_guess_image2_codec(filename)) != AV_CODEC_ID_NONE) { + AVPacket pkt; + int ret; + + ret = av_get_packet(s->pb, &pkt, size); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Error reading cover art.\n"); + return ret; + } + + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = id; + + st->attached_pic = pkt; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + } else { + st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + if (avio_read(pb, st->codec->extradata, size) != size) { + av_freep(&st->codec->extradata); + return AVERROR(EIO); + } + st->codec->extradata_size = size; + st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; + } + } else { + value = av_malloc(size+1); + if (!value) + return AVERROR(ENOMEM); + c = avio_read(pb, value, size); + if (c < 0) { + av_free(value); + return c; + } + value[c] = 0; + av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); + } + return 0; +} + +int64_t ff_ape_parse_tag(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int file_size = avio_size(pb); + uint32_t val, fields, tag_bytes; + uint8_t buf[8]; + int64_t tag_start; + int i; + + if (file_size < APE_TAG_FOOTER_BYTES) + return 0; + + avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET); + + avio_read(pb, buf, 8); /* APETAGEX */ + if (strncmp(buf, APE_TAG_PREAMBLE, 8)) { + return 0; + } + + val = avio_rl32(pb); /* APE tag version */ + if (val > APE_TAG_VERSION) { + av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION); + return 0; + } + + tag_bytes = avio_rl32(pb); /* tag size */ + if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) { + av_log(s, AV_LOG_ERROR, "Tag size is way too big\n"); + return 0; + } + + if (tag_bytes > file_size - APE_TAG_FOOTER_BYTES) { + av_log(s, AV_LOG_ERROR, "Invalid tag size %u.\n", tag_bytes); + return 0; + } + tag_start = file_size - tag_bytes - APE_TAG_FOOTER_BYTES; + + fields = avio_rl32(pb); /* number of fields */ + if (fields > 65536) { + av_log(s, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields); + return 0; + } + + val = avio_rl32(pb); /* flags */ + if (val & APE_TAG_FLAG_IS_HEADER) { + av_log(s, AV_LOG_ERROR, "APE Tag is a header\n"); + return 0; + } + + avio_seek(pb, file_size - tag_bytes, SEEK_SET); + + for (i=0; i + * based upon libdemac from Dave Chapman. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_APETAG_H +#define AVFORMAT_APETAG_H + +#include "avformat.h" + +#define APE_TAG_PREAMBLE "APETAGEX" +#define APE_TAG_VERSION 2000 +#define APE_TAG_FOOTER_BYTES 32 + +/** + * Read and parse an APE tag + * + * @return offset of the tag start in the file + */ +int64_t ff_ape_parse_tag(AVFormatContext *s); + +/** + * Write an APEv2 tag + */ +void ff_ape_write(AVFormatContext *s); + +#endif /* AVFORMAT_APETAG_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apetag.o Binary file ffmpeg/libavformat/apetag.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apetagenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/apetagenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,65 @@ +/* + * APE tag writer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/dict.h" +#include "avio_internal.h" +#include "avformat.h" +#include "apetag.h" + +static int string_is_ascii(const uint8_t *str) +{ + while (*str && *str >= 0x20 && *str <= 0x7e ) str++; + return !*str; +} + +void ff_ape_write(AVFormatContext *s) +{ + int64_t tag_bytes; + AVDictionaryEntry *t = NULL; + AVIOContext *pb = s->pb; + int tags = 0, vlen; + + tag_bytes = avio_tell(s->pb); + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + if (!string_is_ascii(t->key)) { + av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n"); + continue; + } + + vlen = strlen(t->value); + avio_wl32(pb, vlen + 1); + avio_wl32(pb, 0); // flags + avio_put_str(pb, t->key); + avio_put_str(pb, t->value); + tags++; + } + tag_bytes = avio_tell(s->pb) - tag_bytes; + + if (!tags) + return; + + avio_write(pb, APE_TAG_PREAMBLE, 8); + avio_wl32(pb, APE_TAG_VERSION); + avio_wl32(pb, tag_bytes + APE_TAG_FOOTER_BYTES); + avio_wl32(pb, tags); // item count + avio_wl32(pb, 0); // global flags + ffio_fill(pb, 0, 8); // reserved +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apetagenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/apetagenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/apetagenc.o libavformat/apetagenc.o: libavformat/apetagenc.c \ + libavutil/dict.h libavformat/avio_internal.h libavformat/avio.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/common.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/log.h libavformat/version.h \ + libavutil/avutil.h libavformat/url.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavformat/apetag.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/apetagenc.o Binary file ffmpeg/libavformat/apetagenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aqtitledec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aqtitledec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * AQTitle subtitles format demuxer + * + * @see http://web.archive.org/web/20070210095721/http://www.volny.cz/aberka/czech/aqt.html + * @see https://trac.annodex.net/wiki/AQTitle + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; + FFDemuxSubtitlesQueue q; + AVRational frame_rate; +} AQTitleContext; + +static int aqt_probe(AVProbeData *p) +{ + int frame; + const char *ptr = p->buf; + + if (sscanf(ptr, "-->> %d", &frame) == 1) + return AVPROBE_SCORE_MAX / 2; + return 0; +} + +static int aqt_read_header(AVFormatContext *s) +{ + AQTitleContext *aqt = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int new_event = 1; + int64_t pos = 0, frame = AV_NOPTS_VALUE; + AVPacket *sub = NULL; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, aqt->frame_rate.den, aqt->frame_rate.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + + while (!url_feof(s->pb)) { + char line[4096]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (sscanf(line, "-->> %"SCNd64, &frame) == 1) { + new_event = 1; + pos = avio_tell(s->pb); + if (sub) { + sub->duration = frame - sub->pts; + sub = NULL; + } + } else if (*line) { + if (!new_event) { + sub = ff_subtitles_queue_insert(&aqt->q, "\n", 1, 1); + if (!sub) + return AVERROR(ENOMEM); + } + sub = ff_subtitles_queue_insert(&aqt->q, line, strlen(line), !new_event); + if (!sub) + return AVERROR(ENOMEM); + if (new_event) { + sub->pts = frame; + sub->duration = -1; + sub->pos = pos; + } + new_event = 0; + } + } + + ff_subtitles_queue_finalize(&aqt->q); + return 0; +} + +static int aqt_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AQTitleContext *aqt = s->priv_data; + return ff_subtitles_queue_read_packet(&aqt->q, pkt); +} + +static int aqt_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + AQTitleContext *aqt = s->priv_data; + return ff_subtitles_queue_seek(&aqt->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int aqt_read_close(AVFormatContext *s) +{ + AQTitleContext *aqt = s->priv_data; + ff_subtitles_queue_clean(&aqt->q); + return 0; +} + +#define OFFSET(x) offsetof(AQTitleContext, x) +#define SD AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM +static const AVOption aqt_options[] = { + { "subfps", "set the movie frame rate", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=25}, 0, INT_MAX, SD }, + { NULL } +}; + +static const AVClass aqt_class = { + .class_name = "aqtdec", + .item_name = av_default_item_name, + .option = aqt_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_aqtitle_demuxer = { + .name = "aqtitle", + .long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"), + .priv_data_size = sizeof(AQTitleContext), + .read_probe = aqt_probe, + .read_header = aqt_read_header, + .read_packet = aqt_read_packet, + .read_seek2 = aqt_read_seek, + .read_close = aqt_read_close, + .extensions = "aqt", + .priv_class = &aqt_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aqtitledec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aqtitledec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/aqtitledec.o libavformat/aqtitledec.o: \ + libavformat/aqtitledec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aqtitledec.o Binary file ffmpeg/libavformat/aqtitledec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "asf.h" + +const ff_asf_guid ff_asf_header = { + 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C +}; + +const ff_asf_guid ff_asf_file_header = { + 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 +}; + +const ff_asf_guid ff_asf_stream_header = { + 0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 +}; + +const ff_asf_guid ff_asf_ext_stream_header = { + 0xCB, 0xA5, 0xE6, 0x14, 0x72, 0xC6, 0x32, 0x43, 0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A +}; + +const ff_asf_guid ff_asf_audio_stream = { + 0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B +}; + +const ff_asf_guid ff_asf_audio_conceal_none = { + // 0x40, 0xa4, 0xf1, 0x49, 0x4ece, 0x11d0, 0xa3, 0xac, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6 + // New value lifted from avifile + 0x00, 0x57, 0xfb, 0x20, 0x55, 0x5B, 0xCF, 0x11, 0xa8, 0xfd, 0x00, 0x80, 0x5f, 0x5c, 0x44, 0x2b +}; + +const ff_asf_guid ff_asf_audio_conceal_spread = { + 0x50, 0xCD, 0xC3, 0xBF, 0x8F, 0x61, 0xCF, 0x11, 0x8B, 0xB2, 0x00, 0xAA, 0x00, 0xB4, 0xE2, 0x20 +}; + +const ff_asf_guid ff_asf_video_stream = { + 0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B +}; + +const ff_asf_guid ff_asf_jfif_media = { + 0x00, 0xE1, 0x1B, 0xB6, 0x4E, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B +}; + +const ff_asf_guid ff_asf_video_conceal_none = { + 0x00, 0x57, 0xFB, 0x20, 0x55, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B +}; + +const ff_asf_guid ff_asf_command_stream = { + 0xC0, 0xCF, 0xDA, 0x59, 0xE6, 0x59, 0xD0, 0x11, 0xA3, 0xAC, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 +}; + +const ff_asf_guid ff_asf_comment_header = { + 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c +}; + +const ff_asf_guid ff_asf_codec_comment_header = { + 0x40, 0x52, 0xD1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 +}; +const ff_asf_guid ff_asf_codec_comment1_header = { + 0x41, 0x52, 0xd1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xa3, 0xa4, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6 +}; + +const ff_asf_guid ff_asf_data_header = { + 0x36, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c +}; + +const ff_asf_guid ff_asf_head1_guid = { + 0xb5, 0x03, 0xbf, 0x5f, 0x2E, 0xA9, 0xCF, 0x11, 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 +}; + +const ff_asf_guid ff_asf_head2_guid = { + 0x11, 0xd2, 0xd3, 0xab, 0xBA, 0xA9, 0xCF, 0x11, 0x8e, 0xe6, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 +}; + +const ff_asf_guid ff_asf_extended_content_header = { + 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 +}; + +const ff_asf_guid ff_asf_simple_index_header = { + 0x90, 0x08, 0x00, 0x33, 0xB1, 0xE5, 0xCF, 0x11, 0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB +}; + +const ff_asf_guid ff_asf_ext_stream_embed_stream_header = { + 0xe2, 0x65, 0xfb, 0x3a, 0xEF, 0x47, 0xF2, 0x40, 0xac, 0x2c, 0x70, 0xa9, 0x0d, 0x71, 0xd3, 0x43 +}; + +const ff_asf_guid ff_asf_ext_stream_audio_stream = { + 0x9d, 0x8c, 0x17, 0x31, 0xE1, 0x03, 0x28, 0x45, 0xb5, 0x82, 0x3d, 0xf9, 0xdb, 0x22, 0xf5, 0x03 +}; + +const ff_asf_guid ff_asf_metadata_header = { + 0xea, 0xcb, 0xf8, 0xc5, 0xaf, 0x5b, 0x77, 0x48, 0x84, 0x67, 0xaa, 0x8c, 0x44, 0xfa, 0x4c, 0xca +}; + +const ff_asf_guid ff_asf_metadata_library_header = { + 0x94, 0x1c, 0x23, 0x44, 0x98, 0x94, 0xd1, 0x49, 0xa1, 0x41, 0x1d, 0x13, 0x4e, 0x45, 0x70, 0x54 +}; + +const ff_asf_guid ff_asf_marker_header = { + 0x01, 0xCD, 0x87, 0xF4, 0x51, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 +}; + +/* I am not a number !!! This GUID is the one found on the PC used to + * generate the stream */ +const ff_asf_guid ff_asf_my_guid = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const ff_asf_guid ff_asf_language_guid = { + 0xa9, 0x46, 0x43, 0x7c, 0xe0, 0xef, 0xfc, 0x4b, 0xb2, 0x29, 0x39, 0x3e, 0xde, 0x41, 0x5c, 0x85 +}; + +const ff_asf_guid ff_asf_content_encryption = { + 0xfb, 0xb3, 0x11, 0x22, 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e +}; + +const ff_asf_guid ff_asf_ext_content_encryption = { + 0x14, 0xe6, 0x8a, 0x29, 0x22, 0x26, 0x17, 0x4c, 0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c +}; + +const ff_asf_guid ff_asf_digital_signature = { + 0xfc, 0xb3, 0x11, 0x22, 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e +}; + +/* List of official tags at http://msdn.microsoft.com/en-us/library/dd743066(VS.85).aspx */ +const AVMetadataConv ff_asf_metadata_conv[] = { + { "WM/AlbumArtist", "album_artist" }, + { "WM/AlbumTitle", "album" }, + { "Author", "artist" }, + { "Description", "comment" }, + { "WM/Composer", "composer" }, + { "WM/EncodedBy", "encoded_by" }, + { "WM/EncodingSettings", "encoder" }, + { "WM/Genre", "genre" }, + { "WM/Language", "language" }, + { "WM/OriginalFilename", "filename" }, + { "WM/PartOfSet", "disc" }, + { "WM/Publisher", "publisher" }, + { "WM/Tool", "encoder" }, + { "WM/TrackNumber", "track" }, + { "WM/MediaStationCallSign", "service_provider" }, + { "WM/MediaStationName", "service_name" }, +// { "Year" , "date" }, TODO: conversion year<->date + { 0 } +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/asf.o libavformat/asf.o: libavformat/asf.c libavformat/asf.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/metadata.h \ + libavformat/riff.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asf.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_ASF_H +#define AVFORMAT_ASF_H + +#include +#include "avformat.h" +#include "metadata.h" +#include "riff.h" + +#define PACKET_SIZE 3200 + +typedef struct ASFPayload { + uint8_t type; + uint16_t size; +} ASFPayload; + +typedef struct ASFStream { + int num; + unsigned char seq; + /* use for reading */ + AVPacket pkt; + int frag_offset; + int timestamp; + int64_t duration; + + int ds_span; /* descrambling */ + int ds_packet_size; + int ds_chunk_size; + + int64_t packet_pos; + + uint16_t stream_language_index; + + int palette_changed; + uint32_t palette[256]; + + int payload_ext_ct; + ASFPayload payload[8]; +} ASFStream; + +typedef struct ASFMainHeader { + ff_asf_guid guid; ///< generated by client computer + uint64_t file_size; /**< in bytes + * invalid if broadcasting */ + uint64_t create_time; /**< time of creation, in 100-nanosecond units since 1.1.1601 + * invalid if broadcasting */ + uint64_t play_time; /**< play time, in 100-nanosecond units + * invalid if broadcasting */ + uint64_t send_time; /**< time to send file, in 100-nanosecond units + * invalid if broadcasting (could be ignored) */ + uint32_t preroll; /**< timestamp of the first packet, in milliseconds + * if nonzero - subtract from time */ + uint32_t ignore; ///< preroll is 64bit - but let's just ignore it + uint32_t flags; /**< 0x01 - broadcast + * 0x02 - seekable + * rest is reserved should be 0 */ + uint32_t min_pktsize; /**< size of a data packet + * invalid if broadcasting */ + uint32_t max_pktsize; /**< shall be the same as for min_pktsize + * invalid if broadcasting */ + uint32_t max_bitrate; /**< bandwidth of stream in bps + * should be the sum of bitrates of the + * individual media streams */ +} ASFMainHeader; + + +typedef struct ASFIndex { + uint32_t packet_number; + uint16_t packet_count; +} ASFIndex; + +extern const ff_asf_guid ff_asf_header; +extern const ff_asf_guid ff_asf_file_header; +extern const ff_asf_guid ff_asf_stream_header; +extern const ff_asf_guid ff_asf_ext_stream_header; +extern const ff_asf_guid ff_asf_audio_stream; +extern const ff_asf_guid ff_asf_audio_conceal_none; +extern const ff_asf_guid ff_asf_audio_conceal_spread; +extern const ff_asf_guid ff_asf_video_stream; +extern const ff_asf_guid ff_asf_jfif_media; +extern const ff_asf_guid ff_asf_video_conceal_none; +extern const ff_asf_guid ff_asf_command_stream; +extern const ff_asf_guid ff_asf_comment_header; +extern const ff_asf_guid ff_asf_codec_comment_header; +extern const ff_asf_guid ff_asf_codec_comment1_header; +extern const ff_asf_guid ff_asf_data_header; +extern const ff_asf_guid ff_asf_head1_guid; +extern const ff_asf_guid ff_asf_head2_guid; +extern const ff_asf_guid ff_asf_extended_content_header; +extern const ff_asf_guid ff_asf_simple_index_header; +extern const ff_asf_guid ff_asf_ext_stream_embed_stream_header; +extern const ff_asf_guid ff_asf_ext_stream_audio_stream; +extern const ff_asf_guid ff_asf_metadata_header; +extern const ff_asf_guid ff_asf_metadata_library_header; +extern const ff_asf_guid ff_asf_marker_header; +extern const ff_asf_guid ff_asf_my_guid; +extern const ff_asf_guid ff_asf_language_guid; +extern const ff_asf_guid ff_asf_content_encryption; +extern const ff_asf_guid ff_asf_ext_content_encryption; +extern const ff_asf_guid ff_asf_digital_signature; + +extern const AVMetadataConv ff_asf_metadata_conv[]; + +#define ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT 0x80 //1000 0000 + + +// ASF data packet structure +// ========================= +// +// +// ----------------------------------- +// | Error Correction Data | Optional +// ----------------------------------- +// | Payload Parsing Information (PPI) | +// ----------------------------------- +// | Payload Data | +// ----------------------------------- +// | Padding Data | +// ----------------------------------- + + +// PPI_FLAG - Payload parsing information flags +#define ASF_PPI_FLAG_MULTIPLE_PAYLOADS_PRESENT 1 + +#define ASF_PPI_FLAG_SEQUENCE_FIELD_IS_BYTE 0x02 //0000 0010 +#define ASF_PPI_FLAG_SEQUENCE_FIELD_IS_WORD 0x04 //0000 0100 +#define ASF_PPI_FLAG_SEQUENCE_FIELD_IS_DWORD 0x06 //0000 0110 +#define ASF_PPI_MASK_SEQUENCE_FIELD_SIZE 0x06 //0000 0110 + +#define ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE 0x08 //0000 1000 +#define ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD 0x10 //0001 0000 +#define ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_DWORD 0x18 //0001 1000 +#define ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE 0x18 //0001 1000 + +#define ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_BYTE 0x20 //0010 0000 +#define ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_WORD 0x40 //0100 0000 +#define ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_DWORD 0x60 //0110 0000 +#define ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE 0x60 //0110 0000 + +// PL_FLAG - Payload flags +#define ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE 0x01 //0000 0001 +#define ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_WORD 0x02 //0000 0010 +#define ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_DWORD 0x03 //0000 0011 +#define ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE 0x03 //0000 0011 + +#define ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_BYTE 0x04 //0000 0100 +#define ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_WORD 0x08 //0000 1000 +#define ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD 0x0c //0000 1100 +#define ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE 0x0c //0000 1100 + +#define ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE 0x10 //0001 0000 +#define ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_WORD 0x20 //0010 0000 +#define ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_DWORD 0x30 //0011 0000 +#define ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE 0x30 //0011 0000 + +#define ASF_PL_FLAG_STREAM_NUMBER_LENGTH_FIELD_IS_BYTE 0x40 //0100 0000 +#define ASF_PL_MASK_STREAM_NUMBER_LENGTH_FIELD_SIZE 0xc0 //1100 0000 + +#define ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_BYTE 0x40 //0100 0000 +#define ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD 0x80 //1000 0000 +#define ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE 0xc0 //1100 0000 + +#define ASF_PL_FLAG_KEY_FRAME 0x80 //1000 0000 + +extern AVInputFormat ff_asf_demuxer; + +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g); + +#endif /* AVFORMAT_ASF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asf.o Binary file ffmpeg/libavformat/asf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfcrypt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfcrypt.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,185 @@ +/* + * ASF decryption + * Copyright (c) 2007 Reimar Doeffinger + * This is a rewrite of code contained in freeme/freeme2 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/bswap.h" +#include "libavutil/common.h" +#include "libavutil/des.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/rc4.h" +#include "asfcrypt.h" + +/** + * @brief find multiplicative inverse modulo 2 ^ 32 + * @param v number to invert, must be odd! + * @return number so that result * v = 1 (mod 2^32) + */ +static uint32_t inverse(uint32_t v) +{ + // v ^ 3 gives the inverse (mod 16), could also be implemented + // as table etc. (only lowest 4 bits matter!) + uint32_t inverse = v * v * v; + // uses a fixpoint-iteration that doubles the number + // of correct lowest bits each time + inverse *= 2 - v * inverse; + inverse *= 2 - v * inverse; + inverse *= 2 - v * inverse; + return inverse; +} + +/** + * @brief read keys from keybuf into keys + * @param keybuf buffer containing the keys + * @param keys output key array containing the keys for encryption in + * native endianness + */ +static void multiswap_init(const uint8_t keybuf[48], uint32_t keys[12]) +{ + int i; + for (i = 0; i < 12; i++) + keys[i] = AV_RL32(keybuf + (i << 2)) | 1; +} + +/** + * @brief invert the keys so that encryption become decryption keys and + * the other way round. + * @param keys key array of ints to invert + */ +static void multiswap_invert_keys(uint32_t keys[12]) +{ + int i; + for (i = 0; i < 5; i++) + keys[i] = inverse(keys[i]); + for (i = 6; i < 11; i++) + keys[i] = inverse(keys[i]); +} + +static uint32_t multiswap_step(const uint32_t keys[12], uint32_t v) +{ + int i; + v *= keys[0]; + for (i = 1; i < 5; i++) { + v = (v >> 16) | (v << 16); + v *= keys[i]; + } + v += keys[5]; + return v; +} + +static uint32_t multiswap_inv_step(const uint32_t keys[12], uint32_t v) +{ + int i; + v -= keys[5]; + for (i = 4; i > 0; i--) { + v *= keys[i]; + v = (v >> 16) | (v << 16); + } + v *= keys[0]; + return v; +} + +/** + * @brief "MultiSwap" encryption + * @param keys 32 bit numbers in machine endianness, + * 0-4 and 6-10 must be inverted from decryption + * @param key another key, this one must be the same for the decryption + * @param data data to encrypt + * @return encrypted data + */ +static uint64_t multiswap_enc(const uint32_t keys[12], + uint64_t key, uint64_t data) +{ + uint32_t a = data; + uint32_t b = data >> 32; + uint32_t c; + uint32_t tmp; + a += key; + tmp = multiswap_step(keys, a); + b += tmp; + c = (key >> 32) + tmp; + tmp = multiswap_step(keys + 6, b); + c += tmp; + return ((uint64_t)c << 32) | tmp; +} + +/** + * @brief "MultiSwap" decryption + * @param keys 32 bit numbers in machine endianness, + * 0-4 and 6-10 must be inverted from encryption + * @param key another key, this one must be the same as for the encryption + * @param data data to decrypt + * @return decrypted data + */ +static uint64_t multiswap_dec(const uint32_t keys[12], + uint64_t key, uint64_t data) +{ + uint32_t a; + uint32_t b; + uint32_t c = data >> 32; + uint32_t tmp = data; + c -= tmp; + b = multiswap_inv_step(keys + 6, tmp); + tmp = c - (key >> 32); + b -= tmp; + a = multiswap_inv_step(keys, tmp); + a -= key; + return ((uint64_t)b << 32) | a; +} + +void ff_asfcrypt_dec(const uint8_t key[20], uint8_t *data, int len) +{ + struct AVDES des; + struct AVRC4 rc4; + int num_qwords = len >> 3; + uint8_t *qwords = data; + uint64_t rc4buff[8] = { 0 }; + uint64_t packetkey; + uint32_t ms_keys[12]; + uint64_t ms_state; + int i; + if (len < 16) { + for (i = 0; i < len; i++) + data[i] ^= key[i]; + return; + } + + av_rc4_init(&rc4, key, 12 * 8, 1); + av_rc4_crypt(&rc4, (uint8_t *)rc4buff, NULL, sizeof(rc4buff), NULL, 1); + multiswap_init((uint8_t *)rc4buff, ms_keys); + + packetkey = AV_RN64(&qwords[num_qwords * 8 - 8]); + packetkey ^= rc4buff[7]; + av_des_init(&des, key + 12, 64, 1); + av_des_crypt(&des, (uint8_t *)&packetkey, (uint8_t *)&packetkey, 1, NULL, 1); + packetkey ^= rc4buff[6]; + + av_rc4_init(&rc4, (uint8_t *)&packetkey, 64, 1); + av_rc4_crypt(&rc4, data, data, len, NULL, 1); + + ms_state = 0; + for (i = 0; i < num_qwords - 1; i++, qwords += 8) + ms_state = multiswap_enc(ms_keys, ms_state, AV_RL64(qwords)); + multiswap_invert_keys(ms_keys); + packetkey = (packetkey << 32) | (packetkey >> 32); + packetkey = av_le2ne64(packetkey); + packetkey = multiswap_dec(ms_keys, ms_state, packetkey); + AV_WL64(qwords, packetkey); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfcrypt.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfcrypt.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,12 @@ +libavformat/asfcrypt.o libavformat/asfcrypt.o: libavformat/asfcrypt.c \ + libavutil/bswap.h libavutil/avconfig.h libavutil/attributes.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/des.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/rc4.h libavformat/asfcrypt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfcrypt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfcrypt.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,29 @@ +/* + * ASF decryption + * Copyright (c) 2007 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_ASFCRYPT_H +#define AVFORMAT_ASFCRYPT_H + +#include + +void ff_asfcrypt_dec(const uint8_t key[20], uint8_t *data, int len); + +#endif /* AVFORMAT_ASFCRYPT_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfcrypt.o Binary file ffmpeg/libavformat/asfcrypt.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1553 @@ +/* + * ASF compatible demuxer + * Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//#define DEBUG + +#include "libavutil/attributes.h" +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/bswap.h" +#include "libavutil/common.h" +#include "libavutil/dict.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "avio_internal.h" +#include "avlanguage.h" +#include "id3v2.h" +#include "internal.h" +#include "riff.h" +#include "asf.h" +#include "asfcrypt.h" + +typedef struct { + const AVClass *class; + int asfid2avid[128]; ///< conversion table from asf ID 2 AVStream ID + ASFStream streams[128]; ///< it's max number and it's not that big + uint32_t stream_bitrates[128]; ///< max number of streams, bitrate for each (for streaming) + AVRational dar[128]; + char stream_languages[128][6]; ///< max number of streams, language for each (RFC1766, e.g. en-US) + /* non streamed additonnal info */ + /* packet filling */ + int packet_size_left; + /* only for reading */ + uint64_t data_offset; ///< beginning of the first data packet + uint64_t data_object_offset; ///< data object offset (excl. GUID & size) + uint64_t data_object_size; ///< size of the data object + int index_read; + + ASFMainHeader hdr; + + int packet_flags; + int packet_property; + int packet_timestamp; + int packet_segsizetype; + int packet_segments; + int packet_seq; + int packet_replic_size; + int packet_key_frame; + int packet_padsize; + unsigned int packet_frag_offset; + unsigned int packet_frag_size; + int64_t packet_frag_timestamp; + int packet_multi_size; + int packet_obj_size; + int packet_time_delta; + int packet_time_start; + int64_t packet_pos; + + int stream_index; + + ASFStream *asf_st; ///< currently decoded stream + + int no_resync_search; +} ASFContext; + +static const AVOption options[] = { + { "no_resync_search", "Don't try to resynchronize by looking for a certain optional start code", offsetof(ASFContext, no_resync_search), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass asf_class = { + .class_name = "asf demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +#undef NDEBUG +#include + +#define ASF_MAX_STREAMS 127 +#define FRAME_HEADER_SIZE 16 +// Fix Me! FRAME_HEADER_SIZE may be different. (17 is known to be too large) + +#ifdef DEBUG +static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */ + 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2 +}; + +#define PRINT_IF_GUID(g, cmp) \ + if (!ff_guidcmp(g, &cmp)) \ + av_dlog(NULL, "(GUID: %s) ", # cmp) + +static void print_guid(ff_asf_guid *g) +{ + int i; + PRINT_IF_GUID(g, ff_asf_header); + else PRINT_IF_GUID(g, ff_asf_file_header); + else PRINT_IF_GUID(g, ff_asf_stream_header); + else PRINT_IF_GUID(g, ff_asf_audio_stream); + else PRINT_IF_GUID(g, ff_asf_audio_conceal_none); + else PRINT_IF_GUID(g, ff_asf_video_stream); + else PRINT_IF_GUID(g, ff_asf_video_conceal_none); + else PRINT_IF_GUID(g, ff_asf_command_stream); + else PRINT_IF_GUID(g, ff_asf_comment_header); + else PRINT_IF_GUID(g, ff_asf_codec_comment_header); + else PRINT_IF_GUID(g, ff_asf_codec_comment1_header); + else PRINT_IF_GUID(g, ff_asf_data_header); + else PRINT_IF_GUID(g, ff_asf_simple_index_header); + else PRINT_IF_GUID(g, ff_asf_head1_guid); + else PRINT_IF_GUID(g, ff_asf_head2_guid); + else PRINT_IF_GUID(g, ff_asf_my_guid); + else PRINT_IF_GUID(g, ff_asf_ext_stream_header); + else PRINT_IF_GUID(g, ff_asf_extended_content_header); + else PRINT_IF_GUID(g, ff_asf_ext_stream_embed_stream_header); + else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream); + else PRINT_IF_GUID(g, ff_asf_metadata_header); + else PRINT_IF_GUID(g, ff_asf_metadata_library_header); + else PRINT_IF_GUID(g, ff_asf_marker_header); + else PRINT_IF_GUID(g, stream_bitrate_guid); + else PRINT_IF_GUID(g, ff_asf_language_guid); + else + av_dlog(NULL, "(GUID: unknown) "); + for (i = 0; i < 16; i++) + av_dlog(NULL, " 0x%02x,", (*g)[i]); + av_dlog(NULL, "}\n"); +} +#undef PRINT_IF_GUID +#else +#define print_guid(g) +#endif + +static int asf_probe(AVProbeData *pd) +{ + /* check file header */ + if (!ff_guidcmp(pd->buf, &ff_asf_header)) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +/* size of type 2 (BOOL) is 32bit for "Extended Content Description Object" + * but 16 bit for "Metadata Object" and "Metadata Library Object" */ +static int get_value(AVIOContext *pb, int type, int type2_size) +{ + switch (type) { + case 2: + return (type2_size == 32) ? avio_rl32(pb) : avio_rl16(pb); + case 3: + return avio_rl32(pb); + case 4: + return avio_rl64(pb); + case 5: + return avio_rl16(pb); + default: + return INT_MIN; + } +} + +/* MSDN claims that this should be "compatible with the ID3 frame, APIC", + * but in reality this is only loosely similar */ +static int asf_read_picture(AVFormatContext *s, int len) +{ + AVPacket pkt = { 0 }; + const CodecMime *mime = ff_id3v2_mime_tags; + enum AVCodecID id = AV_CODEC_ID_NONE; + char mimetype[64]; + uint8_t *desc = NULL; + AVStream *st = NULL; + int ret, type, picsize, desc_len; + + /* type + picsize + mime + desc */ + if (len < 1 + 4 + 2 + 2) { + av_log(s, AV_LOG_ERROR, "Invalid attached picture size: %d.\n", len); + return AVERROR_INVALIDDATA; + } + + /* picture type */ + type = avio_r8(s->pb); + len--; + if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types) || type < 0) { + av_log(s, AV_LOG_WARNING, "Unknown attached picture type: %d.\n", type); + type = 0; + } + + /* picture data size */ + picsize = avio_rl32(s->pb); + len -= 4; + + /* picture MIME type */ + len -= avio_get_str16le(s->pb, len, mimetype, sizeof(mimetype)); + while (mime->id != AV_CODEC_ID_NONE) { + if (!strncmp(mime->str, mimetype, sizeof(mimetype))) { + id = mime->id; + break; + } + mime++; + } + if (id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_ERROR, "Unknown attached picture mimetype: %s.\n", + mimetype); + return 0; + } + + if (picsize >= len) { + av_log(s, AV_LOG_ERROR, "Invalid attached picture data size: %d >= %d.\n", + picsize, len); + return AVERROR_INVALIDDATA; + } + + /* picture description */ + desc_len = (len - picsize) * 2 + 1; + desc = av_malloc(desc_len); + if (!desc) + return AVERROR(ENOMEM); + len -= avio_get_str16le(s->pb, len - picsize, desc, desc_len); + + ret = av_get_packet(s->pb, &pkt, picsize); + if (ret < 0) + goto fail; + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = id; + st->attached_pic = pkt; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + + if (*desc) + av_dict_set(&st->metadata, "title", desc, AV_DICT_DONT_STRDUP_VAL); + else + av_freep(&desc); + + av_dict_set(&st->metadata, "comment", ff_id3v2_picture_types[type], 0); + + return 0; + +fail: + av_freep(&desc); + av_free_packet(&pkt); + return ret; +} + +static void get_id3_tag(AVFormatContext *s, int len) +{ + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) + ff_id3v2_parse_apic(s, &id3v2_extra_meta); + ff_id3v2_free_extra_meta(&id3v2_extra_meta); +} + +static void get_tag(AVFormatContext *s, const char *key, int type, int len, int type2_size) +{ + char *value; + int64_t off = avio_tell(s->pb); +#define LEN 22 + + if ((unsigned)len >= (UINT_MAX - LEN) / 2) + return; + + value = av_malloc(2 * len + LEN); + if (!value) + goto finish; + + if (type == 0) { // UTF16-LE + avio_get_str16le(s->pb, len, value, 2 * len + 1); + } else if (type == -1) { // ASCII + avio_read(s->pb, value, len); + value[len]=0; + } else if (type == 1) { // byte array + if (!strcmp(key, "WM/Picture")) { // handle cover art + asf_read_picture(s, len); + } else if (!strcmp(key, "ID3")) { // handle ID3 tag + get_id3_tag(s, len); + } else { + av_log(s, AV_LOG_VERBOSE, "Unsupported byte array in tag %s.\n", key); + } + goto finish; + } else if (type > 1 && type <= 5) { // boolean or DWORD or QWORD or WORD + uint64_t num = get_value(s->pb, type, type2_size); + snprintf(value, LEN, "%"PRIu64, num); + } else if (type == 6) { // (don't) handle GUID + av_log(s, AV_LOG_DEBUG, "Unsupported GUID value in tag %s.\n", key); + goto finish; + } else { + av_log(s, AV_LOG_DEBUG, + "Unsupported value type %d in tag %s.\n", type, key); + goto finish; + } + if (*value) + av_dict_set(&s->metadata, key, value, 0); + +finish: + av_freep(&value); + avio_seek(s->pb, off + len, SEEK_SET); +} + +static int asf_read_file_properties(AVFormatContext *s, int64_t size) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + + ff_get_guid(pb, &asf->hdr.guid); + asf->hdr.file_size = avio_rl64(pb); + asf->hdr.create_time = avio_rl64(pb); + avio_rl64(pb); /* number of packets */ + asf->hdr.play_time = avio_rl64(pb); + asf->hdr.send_time = avio_rl64(pb); + asf->hdr.preroll = avio_rl32(pb); + asf->hdr.ignore = avio_rl32(pb); + asf->hdr.flags = avio_rl32(pb); + asf->hdr.min_pktsize = avio_rl32(pb); + asf->hdr.max_pktsize = avio_rl32(pb); + if (asf->hdr.min_pktsize >= (1U << 29)) + return AVERROR_INVALIDDATA; + asf->hdr.max_bitrate = avio_rl32(pb); + s->packet_size = asf->hdr.max_pktsize; + + return 0; +} + +static int asf_read_stream_properties(AVFormatContext *s, int64_t size) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + ASFStream *asf_st; + ff_asf_guid g; + enum AVMediaType type; + int type_specific_size, sizeX; + unsigned int tag1; + int64_t pos1, pos2, start_time; + int test_for_ext_stream_audio, is_dvr_ms_audio = 0; + + if (s->nb_streams == ASF_MAX_STREAMS) { + av_log(s, AV_LOG_ERROR, "too many streams\n"); + return AVERROR(EINVAL); + } + + pos1 = avio_tell(pb); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ + asf_st = av_mallocz(sizeof(ASFStream)); + if (!asf_st) + return AVERROR(ENOMEM); + st->priv_data = asf_st; + start_time = asf->hdr.preroll; + + asf_st->stream_language_index = 128; // invalid stream index means no language info + + if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming... + int64_t fsize = avio_size(pb); + if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 || FFABS(fsize - (int64_t)asf->hdr.file_size) < 10000) + st->duration = asf->hdr.play_time / + (10000000 / 1000) - start_time; + } + ff_get_guid(pb, &g); + + test_for_ext_stream_audio = 0; + if (!ff_guidcmp(&g, &ff_asf_audio_stream)) { + type = AVMEDIA_TYPE_AUDIO; + } else if (!ff_guidcmp(&g, &ff_asf_video_stream)) { + type = AVMEDIA_TYPE_VIDEO; + } else if (!ff_guidcmp(&g, &ff_asf_jfif_media)) { + type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MJPEG; + } else if (!ff_guidcmp(&g, &ff_asf_command_stream)) { + type = AVMEDIA_TYPE_DATA; + } else if (!ff_guidcmp(&g, &ff_asf_ext_stream_embed_stream_header)) { + test_for_ext_stream_audio = 1; + type = AVMEDIA_TYPE_UNKNOWN; + } else { + return -1; + } + ff_get_guid(pb, &g); + avio_skip(pb, 8); /* total_size */ + type_specific_size = avio_rl32(pb); + avio_rl32(pb); + st->id = avio_rl16(pb) & 0x7f; /* stream id */ + // mapping of asf ID to AV stream ID; + asf->asfid2avid[st->id] = s->nb_streams - 1; + + avio_rl32(pb); + + if (test_for_ext_stream_audio) { + ff_get_guid(pb, &g); + if (!ff_guidcmp(&g, &ff_asf_ext_stream_audio_stream)) { + type = AVMEDIA_TYPE_AUDIO; + is_dvr_ms_audio = 1; + ff_get_guid(pb, &g); + avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + ff_get_guid(pb, &g); + avio_rl32(pb); + } + } + + st->codec->codec_type = type; + if (type == AVMEDIA_TYPE_AUDIO) { + int ret = ff_get_wav_header(pb, st->codec, type_specific_size); + if (ret < 0) + return ret; + if (is_dvr_ms_audio) { + // codec_id and codec_tag are unreliable in dvr_ms + // files. Set them later by probing stream. + st->request_probe = 1; + st->codec->codec_tag = 0; + } + if (st->codec->codec_id == AV_CODEC_ID_AAC) + st->need_parsing = AVSTREAM_PARSE_NONE; + else + st->need_parsing = AVSTREAM_PARSE_FULL; + /* We have to init the frame size at some point .... */ + pos2 = avio_tell(pb); + if (size >= (pos2 + 8 - pos1 + 24)) { + asf_st->ds_span = avio_r8(pb); + asf_st->ds_packet_size = avio_rl16(pb); + asf_st->ds_chunk_size = avio_rl16(pb); + avio_rl16(pb); // ds_data_size + avio_r8(pb); // ds_silence_data + } + if (asf_st->ds_span > 1) { + if (!asf_st->ds_chunk_size || + (asf_st->ds_packet_size / asf_st->ds_chunk_size <= 1) || + asf_st->ds_packet_size % asf_st->ds_chunk_size) + asf_st->ds_span = 0; // disable descrambling + } + } else if (type == AVMEDIA_TYPE_VIDEO && + size - (avio_tell(pb) - pos1 + 24) >= 51) { + avio_rl32(pb); + avio_rl32(pb); + avio_r8(pb); + avio_rl16(pb); /* size */ + sizeX = avio_rl32(pb); /* size */ + st->codec->width = avio_rl32(pb); + st->codec->height = avio_rl32(pb); + /* not available for asf */ + avio_rl16(pb); /* panes */ + st->codec->bits_per_coded_sample = avio_rl16(pb); /* depth */ + tag1 = avio_rl32(pb); + avio_skip(pb, 20); + if (sizeX > 40) { + st->codec->extradata_size = ffio_limit(pb, sizeX - 40); + st->codec->extradata = av_mallocz(st->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + } + + /* Extract palette from extradata if bpp <= 8 */ + /* This code assumes that extradata contains only palette */ + /* This is true for all paletted codecs implemented in libavcodec */ + if (st->codec->extradata_size && (st->codec->bits_per_coded_sample <= 8)) { +#if HAVE_BIGENDIAN + int i; + for (i = 0; i < FFMIN(st->codec->extradata_size, AVPALETTE_SIZE) / 4; i++) + asf_st->palette[i] = av_bswap32(((uint32_t *)st->codec->extradata)[i]); +#else + memcpy(asf_st->palette, st->codec->extradata, + FFMIN(st->codec->extradata_size, AVPALETTE_SIZE)); +#endif + asf_st->palette_changed = 1; + } + + st->codec->codec_tag = tag1; + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); + if (tag1 == MKTAG('D', 'V', 'R', ' ')) { + st->need_parsing = AVSTREAM_PARSE_FULL; + /* issue658 contains wrong w/h and MS even puts a fake seq header + * with wrong w/h in extradata while a correct one is in the stream. + * maximum lameness */ + st->codec->width = + st->codec->height = 0; + av_freep(&st->codec->extradata); + st->codec->extradata_size = 0; + } + if (st->codec->codec_id == AV_CODEC_ID_H264) + st->need_parsing = AVSTREAM_PARSE_FULL_ONCE; + } + pos2 = avio_tell(pb); + avio_skip(pb, size - (pos2 - pos1 + 24)); + + return 0; +} + +static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + ff_asf_guid g; + int ext_len, payload_ext_ct, stream_ct, i; + uint32_t leak_rate, stream_num; + unsigned int stream_languageid_index; + + avio_rl64(pb); // starttime + avio_rl64(pb); // endtime + leak_rate = avio_rl32(pb); // leak-datarate + avio_rl32(pb); // bucket-datasize + avio_rl32(pb); // init-bucket-fullness + avio_rl32(pb); // alt-leak-datarate + avio_rl32(pb); // alt-bucket-datasize + avio_rl32(pb); // alt-init-bucket-fullness + avio_rl32(pb); // max-object-size + avio_rl32(pb); // flags (reliable,seekable,no_cleanpoints?,resend-live-cleanpoints, rest of bits reserved) + stream_num = avio_rl16(pb); // stream-num + + stream_languageid_index = avio_rl16(pb); // stream-language-id-index + if (stream_num < 128) + asf->streams[stream_num].stream_language_index = stream_languageid_index; + + avio_rl64(pb); // avg frametime in 100ns units + stream_ct = avio_rl16(pb); // stream-name-count + payload_ext_ct = avio_rl16(pb); // payload-extension-system-count + + if (stream_num < 128) { + asf->stream_bitrates[stream_num] = leak_rate; + asf->streams[stream_num].payload_ext_ct = 0; + } + + for (i = 0; i < stream_ct; i++) { + avio_rl16(pb); + ext_len = avio_rl16(pb); + avio_skip(pb, ext_len); + } + + for (i = 0; i < payload_ext_ct; i++) { + int size; + ff_get_guid(pb, &g); + size = avio_rl16(pb); + ext_len = avio_rl32(pb); + avio_skip(pb, ext_len); + if (stream_num < 128 && i < FF_ARRAY_ELEMS(asf->streams[stream_num].payload)) { + ASFPayload *p = &asf->streams[stream_num].payload[i]; + p->type = g[0]; + p->size = size; + av_log(s, AV_LOG_DEBUG, "Payload extension %x %d\n", g[0], p->size ); + asf->streams[stream_num].payload_ext_ct ++; + } + } + + return 0; +} + +static int asf_read_content_desc(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + int len1, len2, len3, len4, len5; + + len1 = avio_rl16(pb); + len2 = avio_rl16(pb); + len3 = avio_rl16(pb); + len4 = avio_rl16(pb); + len5 = avio_rl16(pb); + get_tag(s, "title", 0, len1, 32); + get_tag(s, "author", 0, len2, 32); + get_tag(s, "copyright", 0, len3, 32); + get_tag(s, "comment", 0, len4, 32); + avio_skip(pb, len5); + + return 0; +} + +static int asf_read_ext_content_desc(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + ASFContext *asf = s->priv_data; + int desc_count, i, ret; + + desc_count = avio_rl16(pb); + for (i = 0; i < desc_count; i++) { + int name_len, value_type, value_len; + char name[1024]; + + name_len = avio_rl16(pb); + if (name_len % 2) // must be even, broken lavf versions wrote len-1 + name_len += 1; + if ((ret = avio_get_str16le(pb, name_len, name, sizeof(name))) < name_len) + avio_skip(pb, name_len - ret); + value_type = avio_rl16(pb); + value_len = avio_rl16(pb); + if (!value_type && value_len % 2) + value_len += 1; + /* My sample has that stream set to 0 maybe that mean the container. + * ASF stream count starts at 1. I am using 0 to the container value + * since it's unused. */ + if (!strcmp(name, "AspectRatioX")) + asf->dar[0].num = get_value(s->pb, value_type, 32); + else if (!strcmp(name, "AspectRatioY")) + asf->dar[0].den = get_value(s->pb, value_type, 32); + else + get_tag(s, name, value_type, value_len, 32); + } + + return 0; +} + +static int asf_read_language_list(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + ASFContext *asf = s->priv_data; + int j, ret; + int stream_count = avio_rl16(pb); + for (j = 0; j < stream_count; j++) { + char lang[6]; + unsigned int lang_len = avio_r8(pb); + if ((ret = avio_get_str16le(pb, lang_len, lang, + sizeof(lang))) < lang_len) + avio_skip(pb, lang_len - ret); + if (j < 128) + av_strlcpy(asf->stream_languages[j], lang, + sizeof(*asf->stream_languages)); + } + + return 0; +} + +static int asf_read_metadata(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + ASFContext *asf = s->priv_data; + int n, stream_num, name_len, value_len; + int ret, i; + n = avio_rl16(pb); + + for (i = 0; i < n; i++) { + char name[1024]; + int value_type; + + avio_rl16(pb); // lang_list_index + stream_num = avio_rl16(pb); + name_len = avio_rl16(pb); + value_type = avio_rl16(pb); /* value_type */ + value_len = avio_rl32(pb); + + if ((ret = avio_get_str16le(pb, name_len, name, sizeof(name))) < name_len) + avio_skip(pb, name_len - ret); + av_dlog(s, "%d stream %d name_len %2d type %d len %4d <%s>\n", + i, stream_num, name_len, value_type, value_len, name); + + if (!strcmp(name, "AspectRatioX")){ + int aspect_x = get_value(s->pb, value_type, 16); + if(stream_num < 128) + asf->dar[stream_num].num = aspect_x; + } else if(!strcmp(name, "AspectRatioY")){ + int aspect_y = get_value(s->pb, value_type, 16); + if(stream_num < 128) + asf->dar[stream_num].den = aspect_y; + } else { + get_tag(s, name, value_type, value_len, 16); + } + } + + return 0; +} + +static int asf_read_marker(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + int i, count, name_len, ret; + char name[1024]; + + avio_rl64(pb); // reserved 16 bytes + avio_rl64(pb); // ... + count = avio_rl32(pb); // markers count + avio_rl16(pb); // reserved 2 bytes + name_len = avio_rl16(pb); // name length + for (i = 0; i < name_len; i++) + avio_r8(pb); // skip the name + + for (i = 0; i < count; i++) { + int64_t pres_time; + int name_len; + + avio_rl64(pb); // offset, 8 bytes + pres_time = avio_rl64(pb); // presentation time + avio_rl16(pb); // entry length + avio_rl32(pb); // send time + avio_rl32(pb); // flags + name_len = avio_rl32(pb); // name length + if ((ret = avio_get_str16le(pb, name_len * 2, name, + sizeof(name))) < name_len) + avio_skip(pb, name_len - ret); + avpriv_new_chapter(s, i, (AVRational) { 1, 10000000 }, pres_time, + AV_NOPTS_VALUE, name); + } + + return 0; +} + +static int asf_read_header(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + ff_asf_guid g; + AVIOContext *pb = s->pb; + int i; + int64_t gsize; + + ff_get_guid(pb, &g); + if (ff_guidcmp(&g, &ff_asf_header)) + return AVERROR_INVALIDDATA; + avio_rl64(pb); + avio_rl32(pb); + avio_r8(pb); + avio_r8(pb); + memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid)); + for (;;) { + uint64_t gpos = avio_tell(pb); + ff_get_guid(pb, &g); + gsize = avio_rl64(pb); + print_guid(&g); + if (!ff_guidcmp(&g, &ff_asf_data_header)) { + asf->data_object_offset = avio_tell(pb); + /* If not streaming, gsize is not unlimited (how?), + * and there is enough space in the file.. */ + if (!(asf->hdr.flags & 0x01) && gsize >= 100) + asf->data_object_size = gsize - 24; + else + asf->data_object_size = (uint64_t)-1; + break; + } + if (gsize < 24) + return AVERROR_INVALIDDATA; + if (!ff_guidcmp(&g, &ff_asf_file_header)) { + int ret = asf_read_file_properties(s, gsize); + if (ret < 0) + return ret; + } else if (!ff_guidcmp(&g, &ff_asf_stream_header)) { + asf_read_stream_properties(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_comment_header)) { + asf_read_content_desc(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_language_guid)) { + asf_read_language_list(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_extended_content_header)) { + asf_read_ext_content_desc(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_metadata_header)) { + asf_read_metadata(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_metadata_library_header)) { + asf_read_metadata(s, gsize); + } else if (!ff_guidcmp(&g, &ff_asf_ext_stream_header)) { + asf_read_ext_stream_properties(s, gsize); + + // there could be a optional stream properties object to follow + // if so the next iteration will pick it up + continue; + } else if (!ff_guidcmp(&g, &ff_asf_head1_guid)) { + ff_get_guid(pb, &g); + avio_skip(pb, 6); + continue; + } else if (!ff_guidcmp(&g, &ff_asf_marker_header)) { + asf_read_marker(s, gsize); + } else if (url_feof(pb)) { + return AVERROR_EOF; + } else { + if (!s->keylen) { + if (!ff_guidcmp(&g, &ff_asf_content_encryption)) { + unsigned int len; + AVPacket pkt; + av_log(s, AV_LOG_WARNING, + "DRM protected stream detected, decoding will likely fail!\n"); + len= avio_rl32(pb); + av_log(s, AV_LOG_DEBUG, "Secret data:\n"); + av_get_packet(pb, &pkt, len); av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size); av_free_packet(&pkt); + len= avio_rl32(pb); + get_tag(s, "ASF_Protection_Type", -1, len, 32); + len= avio_rl32(pb); + get_tag(s, "ASF_Key_ID", -1, len, 32); + len= avio_rl32(pb); + get_tag(s, "ASF_License_URL", -1, len, 32); + } else if (!ff_guidcmp(&g, &ff_asf_ext_content_encryption)) { + av_log(s, AV_LOG_WARNING, + "Ext DRM protected stream detected, decoding will likely fail!\n"); + av_dict_set(&s->metadata, "encryption", "ASF Extended Content Encryption", 0); + } else if (!ff_guidcmp(&g, &ff_asf_digital_signature)) { + av_log(s, AV_LOG_INFO, "Digital signature detected!\n"); + } + } + } + if (avio_tell(pb) != gpos + gsize) + av_log(s, AV_LOG_DEBUG, + "gpos mismatch our pos=%"PRIu64", end=%"PRId64"\n", + avio_tell(pb) - gpos, gsize); + avio_seek(pb, gpos + gsize, SEEK_SET); + } + ff_get_guid(pb, &g); + avio_rl64(pb); + avio_r8(pb); + avio_r8(pb); + if (url_feof(pb)) + return AVERROR_EOF; + asf->data_offset = avio_tell(pb); + asf->packet_size_left = 0; + + for (i = 0; i < 128; i++) { + int stream_num = asf->asfid2avid[i]; + if (stream_num >= 0) { + AVStream *st = s->streams[stream_num]; + if (!st->codec->bit_rate) + st->codec->bit_rate = asf->stream_bitrates[i]; + if (asf->dar[i].num > 0 && asf->dar[i].den > 0) { + av_reduce(&st->sample_aspect_ratio.num, + &st->sample_aspect_ratio.den, + asf->dar[i].num, asf->dar[i].den, INT_MAX); + } else if ((asf->dar[0].num > 0) && (asf->dar[0].den > 0) && + // Use ASF container value if the stream doesn't set AR. + (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) + av_reduce(&st->sample_aspect_ratio.num, + &st->sample_aspect_ratio.den, + asf->dar[0].num, asf->dar[0].den, INT_MAX); + + av_dlog(s, "i=%d, st->codec->codec_type:%d, asf->dar %d:%d sar=%d:%d\n", + i, st->codec->codec_type, asf->dar[i].num, asf->dar[i].den, + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); + + // copy and convert language codes to the frontend + if (asf->streams[i].stream_language_index < 128) { + const char *rfc1766 = asf->stream_languages[asf->streams[i].stream_language_index]; + if (rfc1766 && strlen(rfc1766) > 1) { + const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any + const char *iso6392 = av_convert_lang_to(primary_tag, + AV_LANG_ISO639_2_BIBL); + if (iso6392) + av_dict_set(&st->metadata, "language", iso6392, 0); + } + } + } + } + + ff_metadata_conv(&s->metadata, NULL, ff_asf_metadata_conv); + + return 0; +} + +#define DO_2BITS(bits, var, defval) \ + switch (bits & 3) { \ + case 3: \ + var = avio_rl32(pb); \ + rsize += 4; \ + break; \ + case 2: \ + var = avio_rl16(pb); \ + rsize += 2; \ + break; \ + case 1: \ + var = avio_r8(pb); \ + rsize++; \ + break; \ + default: \ + var = defval; \ + break; \ + } + +/** + * Load a single ASF packet into the demuxer. + * @param s demux context + * @param pb context to read data from + * @return 0 on success, <0 on error + */ +static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb) +{ + ASFContext *asf = s->priv_data; + uint32_t packet_length, padsize; + int rsize = 8; + int c, d, e, off; + + // if we do not know packet size, allow skipping up to 32 kB + off = 32768; + if (asf->no_resync_search) + off = 3; + else if (s->packet_size > 0) + off = (avio_tell(pb) - s->data_offset) % s->packet_size + 3; + + c = d = e = -1; + while (off-- > 0) { + c = d; + d = e; + e = avio_r8(pb); + if (c == 0x82 && !d && !e) + break; + } + + if (c != 0x82) { + /* This code allows handling of -EAGAIN at packet boundaries (i.e. + * if the packet sync code above triggers -EAGAIN). This does not + * imply complete -EAGAIN handling support at random positions in + * the stream. */ + if (pb->error == AVERROR(EAGAIN)) + return AVERROR(EAGAIN); + if (!url_feof(pb)) + av_log(s, AV_LOG_ERROR, + "ff asf bad header %x at:%"PRId64"\n", c, avio_tell(pb)); + } + if ((c & 0x8f) == 0x82) { + if (d || e) { + if (!url_feof(pb)) + av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n"); + return AVERROR_INVALIDDATA; + } + c = avio_r8(pb); + d = avio_r8(pb); + rsize += 3; + } else if(!url_feof(pb)) { + avio_seek(pb, -1, SEEK_CUR); // FIXME + } + + asf->packet_flags = c; + asf->packet_property = d; + + DO_2BITS(asf->packet_flags >> 5, packet_length, s->packet_size); + DO_2BITS(asf->packet_flags >> 1, padsize, 0); // sequence ignored + DO_2BITS(asf->packet_flags >> 3, padsize, 0); // padding length + + // the following checks prevent overflows and infinite loops + if (!packet_length || packet_length >= (1U << 29)) { + av_log(s, AV_LOG_ERROR, + "invalid packet_length %d at:%"PRId64"\n", + packet_length, avio_tell(pb)); + return AVERROR_INVALIDDATA; + } + if (padsize >= packet_length) { + av_log(s, AV_LOG_ERROR, + "invalid padsize %d at:%"PRId64"\n", padsize, avio_tell(pb)); + return AVERROR_INVALIDDATA; + } + + asf->packet_timestamp = avio_rl32(pb); + avio_rl16(pb); /* duration */ + // rsize has at least 11 bytes which have to be present + + if (asf->packet_flags & 0x01) { + asf->packet_segsizetype = avio_r8(pb); + rsize++; + asf->packet_segments = asf->packet_segsizetype & 0x3f; + } else { + asf->packet_segments = 1; + asf->packet_segsizetype = 0x80; + } + if (rsize > packet_length - padsize) { + asf->packet_size_left = 0; + av_log(s, AV_LOG_ERROR, + "invalid packet header length %d for pktlen %d-%d at %"PRId64"\n", + rsize, packet_length, padsize, avio_tell(pb)); + return AVERROR_INVALIDDATA; + } + asf->packet_size_left = packet_length - padsize - rsize; + if (packet_length < asf->hdr.min_pktsize) + padsize += asf->hdr.min_pktsize - packet_length; + asf->packet_padsize = padsize; + av_dlog(s, "packet: size=%d padsize=%d left=%d\n", + s->packet_size, asf->packet_padsize, asf->packet_size_left); + return 0; +} + +/** + * + * @return <0 if error + */ +static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb) +{ + ASFContext *asf = s->priv_data; + ASFStream *asfst; + int rsize = 1; + int num = avio_r8(pb); + int i; + int64_t ts0, ts1 av_unused; + + asf->packet_segments--; + asf->packet_key_frame = num >> 7; + asf->stream_index = asf->asfid2avid[num & 0x7f]; + asfst = &asf->streams[num & 0x7f]; + // sequence should be ignored! + DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0); + DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0); + DO_2BITS(asf->packet_property, asf->packet_replic_size, 0); + av_dlog(asf, "key:%d stream:%d seq:%d offset:%d replic_size:%d\n", + asf->packet_key_frame, asf->stream_index, asf->packet_seq, + asf->packet_frag_offset, asf->packet_replic_size); + if (rsize+(int64_t)asf->packet_replic_size > asf->packet_size_left) { + av_log(s, AV_LOG_ERROR, "packet_replic_size %d is invalid\n", asf->packet_replic_size); + return AVERROR_INVALIDDATA; + } + if (asf->packet_replic_size >= 8) { + int64_t end = avio_tell(pb) + asf->packet_replic_size; + AVRational aspect; + asf->packet_obj_size = avio_rl32(pb); + if (asf->packet_obj_size >= (1 << 24) || asf->packet_obj_size <= 0) { + av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n"); + asf->packet_obj_size = 0; + return AVERROR_INVALIDDATA; + } + asf->packet_frag_timestamp = avio_rl32(pb); // timestamp + + for (i = 0; i < asfst->payload_ext_ct; i++) { + ASFPayload *p = &asfst->payload[i]; + int size = p->size; + int64_t payend; + if (size == 0xFFFF) + size = avio_rl16(pb); + payend = avio_tell(pb) + size; + if (payend > end) { + av_log(s, AV_LOG_ERROR, "too long payload\n"); + break; + } + switch (p->type) { + case 0x50: +// duration = avio_rl16(pb); + break; + case 0x54: + aspect.num = avio_r8(pb); + aspect.den = avio_r8(pb); + if (aspect.num > 0 && aspect.den > 0 && asf->stream_index >= 0) { + s->streams[asf->stream_index]->sample_aspect_ratio = aspect; + } + break; + case 0x2A: + avio_skip(pb, 8); + ts0 = avio_rl64(pb); + ts1 = avio_rl64(pb); + if (ts0!= -1) asf->packet_frag_timestamp = ts0/10000; + else asf->packet_frag_timestamp = AV_NOPTS_VALUE; + break; + case 0x5B: + case 0xB7: + case 0xCC: + case 0xC0: + case 0xA0: + //unknown + break; + } + avio_seek(pb, payend, SEEK_SET); + } + + avio_seek(pb, end, SEEK_SET); + rsize += asf->packet_replic_size; // FIXME - check validity + } else if (asf->packet_replic_size == 1) { + // multipacket - frag_offset is beginning timestamp + asf->packet_time_start = asf->packet_frag_offset; + asf->packet_frag_offset = 0; + asf->packet_frag_timestamp = asf->packet_timestamp; + + asf->packet_time_delta = avio_r8(pb); + rsize++; + } else if (asf->packet_replic_size != 0) { + av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n", + asf->packet_replic_size); + return AVERROR_INVALIDDATA; + } + if (asf->packet_flags & 0x01) { + DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal + if (rsize > asf->packet_size_left) { + av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n"); + return AVERROR_INVALIDDATA; + } else if (asf->packet_frag_size > asf->packet_size_left - rsize) { + if (asf->packet_frag_size > asf->packet_size_left - rsize + asf->packet_padsize) { + av_log(s, AV_LOG_ERROR, "packet_frag_size is invalid (%d-%d)\n", + asf->packet_size_left, rsize); + return AVERROR_INVALIDDATA; + } else { + int diff = asf->packet_frag_size - (asf->packet_size_left - rsize); + asf->packet_size_left += diff; + asf->packet_padsize -= diff; + } + } + } else { + asf->packet_frag_size = asf->packet_size_left - rsize; + } + if (asf->packet_replic_size == 1) { + asf->packet_multi_size = asf->packet_frag_size; + if (asf->packet_multi_size > asf->packet_size_left) + return AVERROR_INVALIDDATA; + } + asf->packet_size_left -= rsize; + + return 0; +} + +/** + * Parse data from individual ASF packets (which were previously loaded + * with asf_get_packet()). + * @param s demux context + * @param pb context to read data from + * @param pkt pointer to store packet data into + * @return 0 if data was stored in pkt, <0 on error or 1 if more ASF + * packets need to be loaded (through asf_get_packet()) + */ +static int ff_asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +{ + ASFContext *asf = s->priv_data; + ASFStream *asf_st = 0; + for (;;) { + int ret; + if (url_feof(pb)) + return AVERROR_EOF; + + if (asf->packet_size_left < FRAME_HEADER_SIZE || + asf->packet_segments < 1) { + int ret = asf->packet_size_left + asf->packet_padsize; + + assert(ret >= 0); + /* fail safe */ + avio_skip(pb, ret); + + asf->packet_pos = avio_tell(pb); + if (asf->data_object_size != (uint64_t)-1 && + (asf->packet_pos - asf->data_object_offset >= asf->data_object_size)) + return AVERROR_EOF; /* Do not exceed the size of the data object */ + return 1; + } + if (asf->packet_time_start == 0) { + if (asf_read_frame_header(s, pb) < 0) { + asf->packet_segments = 0; + continue; + } + if (asf->stream_index < 0 || + s->streams[asf->stream_index]->discard >= AVDISCARD_ALL || + (!asf->packet_key_frame && + s->streams[asf->stream_index]->discard >= AVDISCARD_NONKEY)) { + asf->packet_time_start = 0; + /* unhandled packet (should not happen) */ + avio_skip(pb, asf->packet_frag_size); + asf->packet_size_left -= asf->packet_frag_size; + if (asf->stream_index < 0) + av_log(s, AV_LOG_ERROR, "ff asf skip %d (unknown stream)\n", + asf->packet_frag_size); + continue; + } + asf->asf_st = s->streams[asf->stream_index]->priv_data; + } + asf_st = asf->asf_st; + av_assert0(asf_st); + + if (asf->packet_replic_size == 1) { + // frag_offset is here used as the beginning timestamp + asf->packet_frag_timestamp = asf->packet_time_start; + asf->packet_time_start += asf->packet_time_delta; + asf->packet_obj_size = asf->packet_frag_size = avio_r8(pb); + asf->packet_size_left--; + asf->packet_multi_size--; + if (asf->packet_multi_size < asf->packet_obj_size) { + asf->packet_time_start = 0; + avio_skip(pb, asf->packet_multi_size); + asf->packet_size_left -= asf->packet_multi_size; + continue; + } + asf->packet_multi_size -= asf->packet_obj_size; + } + if (asf_st->frag_offset + asf->packet_frag_size <= asf_st->pkt.size && + asf_st->frag_offset + asf->packet_frag_size > asf->packet_obj_size) { + av_log(s, AV_LOG_INFO, "ignoring invalid packet_obj_size (%d %d %d %d)\n", + asf_st->frag_offset, asf->packet_frag_size, + asf->packet_obj_size, asf_st->pkt.size); + asf->packet_obj_size = asf_st->pkt.size; + } + + if (asf_st->pkt.size != asf->packet_obj_size || + // FIXME is this condition sufficient? + asf_st->frag_offset + asf->packet_frag_size > asf_st->pkt.size) { + if (asf_st->pkt.data) { + av_log(s, AV_LOG_INFO, + "freeing incomplete packet size %d, new %d\n", + asf_st->pkt.size, asf->packet_obj_size); + asf_st->frag_offset = 0; + av_free_packet(&asf_st->pkt); + } + /* new packet */ + av_new_packet(&asf_st->pkt, asf->packet_obj_size); + asf_st->seq = asf->packet_seq; + asf_st->pkt.dts = asf->packet_frag_timestamp - asf->hdr.preroll; + asf_st->pkt.stream_index = asf->stream_index; + asf_st->pkt.pos = asf_st->packet_pos = asf->packet_pos; + + if (asf_st->pkt.data && asf_st->palette_changed) { + uint8_t *pal; + pal = av_packet_new_side_data(&asf_st->pkt, AV_PKT_DATA_PALETTE, + AVPALETTE_SIZE); + if (!pal) { + av_log(s, AV_LOG_ERROR, "Cannot append palette to packet\n"); + } else { + memcpy(pal, asf_st->palette, AVPALETTE_SIZE); + asf_st->palette_changed = 0; + } + } + av_dlog(asf, "new packet: stream:%d key:%d packet_key:%d audio:%d size:%d\n", + asf->stream_index, asf->packet_key_frame, + asf_st->pkt.flags & AV_PKT_FLAG_KEY, + s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO, + asf->packet_obj_size); + if (s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + asf->packet_key_frame = 1; + if (asf->packet_key_frame) + asf_st->pkt.flags |= AV_PKT_FLAG_KEY; + } + + /* read data */ + av_dlog(asf, "READ PACKET s:%d os:%d o:%d,%d l:%d DATA:%p\n", + s->packet_size, asf_st->pkt.size, asf->packet_frag_offset, + asf_st->frag_offset, asf->packet_frag_size, asf_st->pkt.data); + asf->packet_size_left -= asf->packet_frag_size; + if (asf->packet_size_left < 0) + continue; + + if (asf->packet_frag_offset >= asf_st->pkt.size || + asf->packet_frag_size > asf_st->pkt.size - asf->packet_frag_offset) { + av_log(s, AV_LOG_ERROR, + "packet fragment position invalid %u,%u not in %u\n", + asf->packet_frag_offset, asf->packet_frag_size, + asf_st->pkt.size); + continue; + } + + ret = avio_read(pb, asf_st->pkt.data + asf->packet_frag_offset, + asf->packet_frag_size); + if (ret != asf->packet_frag_size) { + if (ret < 0 || asf->packet_frag_offset + ret == 0) + return ret < 0 ? ret : AVERROR_EOF; + + if (asf_st->ds_span > 1) { + // scrambling, we can either drop it completely or fill the remainder + // TODO: should we fill the whole packet instead of just the current + // fragment? + memset(asf_st->pkt.data + asf->packet_frag_offset + ret, 0, + asf->packet_frag_size - ret); + ret = asf->packet_frag_size; + } else { + // no scrambling, so we can return partial packets + av_shrink_packet(&asf_st->pkt, asf->packet_frag_offset + ret); + } + } + if (s->key && s->keylen == 20) + ff_asfcrypt_dec(s->key, asf_st->pkt.data + asf->packet_frag_offset, + ret); + asf_st->frag_offset += ret; + /* test if whole packet is read */ + if (asf_st->frag_offset == asf_st->pkt.size) { + // workaround for macroshit radio DVR-MS files + if (s->streams[asf->stream_index]->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO && + asf_st->pkt.size > 100) { + int i; + for (i = 0; i < asf_st->pkt.size && !asf_st->pkt.data[i]; i++) + ; + if (i == asf_st->pkt.size) { + av_log(s, AV_LOG_DEBUG, "discarding ms fart\n"); + asf_st->frag_offset = 0; + av_free_packet(&asf_st->pkt); + continue; + } + } + + /* return packet */ + if (asf_st->ds_span > 1) { + if (asf_st->pkt.size != asf_st->ds_packet_size * asf_st->ds_span) { + av_log(s, AV_LOG_ERROR, + "pkt.size != ds_packet_size * ds_span (%d %d %d)\n", + asf_st->pkt.size, asf_st->ds_packet_size, + asf_st->ds_span); + } else { + /* packet descrambling */ + AVBufferRef *buf = av_buffer_alloc(asf_st->pkt.size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (buf) { + uint8_t *newdata = buf->data; + int offset = 0; + memset(newdata + asf_st->pkt.size, 0, + FF_INPUT_BUFFER_PADDING_SIZE); + while (offset < asf_st->pkt.size) { + int off = offset / asf_st->ds_chunk_size; + int row = off / asf_st->ds_span; + int col = off % asf_st->ds_span; + int idx = row + col * asf_st->ds_packet_size / asf_st->ds_chunk_size; + assert(offset + asf_st->ds_chunk_size <= asf_st->pkt.size); + assert(idx + 1 <= asf_st->pkt.size / asf_st->ds_chunk_size); + memcpy(newdata + offset, + asf_st->pkt.data + idx * asf_st->ds_chunk_size, + asf_st->ds_chunk_size); + offset += asf_st->ds_chunk_size; + } + av_buffer_unref(&asf_st->pkt.buf); + asf_st->pkt.buf = buf; + asf_st->pkt.data = buf->data; + } + } + } + asf_st->frag_offset = 0; + *pkt = asf_st->pkt; +#if FF_API_DESTRUCT_PACKET + asf_st->pkt.destruct = NULL; +#endif + asf_st->pkt.buf = 0; + asf_st->pkt.size = 0; + asf_st->pkt.data = 0; + asf_st->pkt.side_data_elems = 0; + asf_st->pkt.side_data = NULL; + break; // packet completed + } + } + return 0; +} + +static int asf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASFContext *asf = s->priv_data; + + for (;;) { + int ret; + + /* parse cached packets, if any */ + if ((ret = ff_asf_parse_packet(s, s->pb, pkt)) <= 0) + return ret; + if ((ret = ff_asf_get_packet(s, s->pb)) < 0) + assert(asf->packet_size_left < FRAME_HEADER_SIZE || + asf->packet_segments < 1); + asf->packet_time_start = 0; + } +} + +// Added to support seeking after packets have been read +// If information is not reset, read_packet fails due to +// leftover information from previous reads +static void asf_reset_header(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + ASFStream *asf_st; + int i; + + asf->packet_size_left = 0; + asf->packet_segments = 0; + asf->packet_flags = 0; + asf->packet_property = 0; + asf->packet_timestamp = 0; + asf->packet_segsizetype = 0; + asf->packet_segments = 0; + asf->packet_seq = 0; + asf->packet_replic_size = 0; + asf->packet_key_frame = 0; + asf->packet_padsize = 0; + asf->packet_frag_offset = 0; + asf->packet_frag_size = 0; + asf->packet_frag_timestamp = 0; + asf->packet_multi_size = 0; + asf->packet_obj_size = 0; + asf->packet_time_delta = 0; + asf->packet_time_start = 0; + + for (i = 0; i < s->nb_streams; i++) { + asf_st = s->streams[i]->priv_data; + if (!asf_st) + continue; + av_free_packet(&asf_st->pkt); + asf_st->frag_offset = 0; + asf_st->seq = 0; + } + asf->asf_st = NULL; +} + +static int asf_read_close(AVFormatContext *s) +{ + asf_reset_header(s); + + return 0; +} + +static int64_t asf_read_pts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + AVPacket pkt1, *pkt = &pkt1; + ASFStream *asf_st; + int64_t pts; + int64_t pos = *ppos; + int i; + int64_t start_pos[ASF_MAX_STREAMS]; + + for (i = 0; i < s->nb_streams; i++) + start_pos[i] = pos; + + if (s->packet_size > 0) + pos = (pos + s->packet_size - 1 - s->data_offset) / + s->packet_size * s->packet_size + + s->data_offset; + *ppos = pos; + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + asf_reset_header(s); + for (;;) { + if (av_read_frame(s, pkt) < 0) { + av_log(s, AV_LOG_INFO, "asf_read_pts failed\n"); + return AV_NOPTS_VALUE; + } + + pts = pkt->dts; + + av_free_packet(pkt); + if (pkt->flags & AV_PKT_FLAG_KEY) { + i = pkt->stream_index; + + asf_st = s->streams[i]->priv_data; + av_assert0(asf_st); + +// assert((asf_st->packet_pos - s->data_offset) % s->packet_size == 0); + pos = asf_st->packet_pos; + + av_add_index_entry(s->streams[i], pos, pts, pkt->size, + pos - start_pos[i] + 1, AVINDEX_KEYFRAME); + start_pos[i] = asf_st->packet_pos + 1; + + if (pkt->stream_index == stream_index) + break; + } + } + + *ppos = pos; + return pts; +} + +static void asf_build_simple_index(AVFormatContext *s, int stream_index) +{ + ff_asf_guid g; + ASFContext *asf = s->priv_data; + int64_t current_pos = avio_tell(s->pb); + + if(avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET) < 0) { + asf->index_read= -1; + return; + } + + ff_get_guid(s->pb, &g); + + /* the data object can be followed by other top-level objects, + * skip them until the simple index object is reached */ + while (ff_guidcmp(&g, &ff_asf_simple_index_header)) { + int64_t gsize = avio_rl64(s->pb); + if (gsize < 24 || url_feof(s->pb)) { + avio_seek(s->pb, current_pos, SEEK_SET); + asf->index_read= -1; + return; + } + avio_skip(s->pb, gsize - 24); + ff_get_guid(s->pb, &g); + } + + { + int64_t itime, last_pos = -1; + int pct, ict; + int i; + int64_t av_unused gsize = avio_rl64(s->pb); + ff_get_guid(s->pb, &g); + itime = avio_rl64(s->pb); + pct = avio_rl32(s->pb); + ict = avio_rl32(s->pb); + av_log(s, AV_LOG_DEBUG, + "itime:0x%"PRIx64", pct:%d, ict:%d\n", itime, pct, ict); + + for (i = 0; i < ict; i++) { + int pktnum = avio_rl32(s->pb); + int pktct = avio_rl16(s->pb); + int64_t pos = s->data_offset + s->packet_size * (int64_t)pktnum; + int64_t index_pts = FFMAX(av_rescale(itime, i, 10000) - asf->hdr.preroll, 0); + + if (pos != last_pos) { + av_log(s, AV_LOG_DEBUG, "pktnum:%d, pktct:%d pts: %"PRId64"\n", + pktnum, pktct, index_pts); + av_add_index_entry(s->streams[stream_index], pos, index_pts, + s->packet_size, 0, AVINDEX_KEYFRAME); + last_pos = pos; + } + } + asf->index_read = ict > 1; + } + avio_seek(s->pb, current_pos, SEEK_SET); +} + +static int asf_read_seek(AVFormatContext *s, int stream_index, + int64_t pts, int flags) +{ + ASFContext *asf = s->priv_data; + AVStream *st = s->streams[stream_index]; + + if (s->packet_size <= 0) + return -1; + + /* Try using the protocol's read_seek if available */ + if (s->pb) { + int ret = avio_seek_time(s->pb, stream_index, pts, flags); + if (ret >= 0) + asf_reset_header(s); + if (ret != AVERROR(ENOSYS)) + return ret; + } + + if (!asf->index_read) + asf_build_simple_index(s, stream_index); + + if ((asf->index_read > 0 && st->index_entries)) { + int index = av_index_search_timestamp(st, pts, flags); + if (index >= 0) { + /* find the position */ + uint64_t pos = st->index_entries[index].pos; + + /* do the seek */ + av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos); + if(avio_seek(s->pb, pos, SEEK_SET) < 0) + return -1; + asf_reset_header(s); + return 0; + } + } + /* no index or seeking by index failed */ + if (ff_seek_frame_binary(s, stream_index, pts, flags) < 0) + return -1; + asf_reset_header(s); + return 0; +} + +AVInputFormat ff_asf_demuxer = { + .name = "asf", + .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .priv_data_size = sizeof(ASFContext), + .read_probe = asf_probe, + .read_header = asf_read_header, + .read_packet = asf_read_packet, + .read_close = asf_read_close, + .read_seek = asf_read_seek, + .read_timestamp = asf_read_pts, + .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH, + .priv_class = &asf_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/asfdec.o libavformat/asfdec.o: libavformat/asfdec.c \ + libavutil/attributes.h libavutil/avassert.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avstring.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/common.h libavutil/dict.h libavutil/mathematics.h \ + libavutil/opt.h libavutil/samplefmt.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/log.h libavformat/avio.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/avlanguage.h libavformat/id3v2.h \ + libavformat/internal.h libavformat/metadata.h libavformat/riff.h \ + libavformat/asf.h libavformat/asfcrypt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfdec.o Binary file ffmpeg/libavformat/asfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,915 @@ +/* + * ASF muxer + * Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/dict.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "riff.h" +#include "asf.h" + +#undef NDEBUG +#include + + +#define ASF_INDEXED_INTERVAL 10000000 +#define ASF_INDEX_BLOCK (1<<9) + +#define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 +#define ASF_PACKET_ERROR_CORRECTION_FLAGS \ + (ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT | \ + ASF_PACKET_ERROR_CORRECTION_DATA_SIZE) + +#if (ASF_PACKET_ERROR_CORRECTION_FLAGS != 0) +# define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 1 +#else +# define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 0 +#endif + +#define ASF_PPI_PROPERTY_FLAGS \ + (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE | \ + ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD | \ + ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE | \ + ASF_PL_FLAG_STREAM_NUMBER_LENGTH_FIELD_IS_BYTE) + +#define ASF_PPI_LENGTH_TYPE_FLAGS 0 + +#define ASF_PAYLOAD_FLAGS ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD + +#if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) +# define ASF_PPI_SEQUENCE_FIELD_SIZE 1 +#endif +#if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) +# define ASF_PPI_SEQUENCE_FIELD_SIZE 2 +#endif +#if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) +# define ASF_PPI_SEQUENCE_FIELD_SIZE 4 +#endif +#ifndef ASF_PPI_SEQUENCE_FIELD_SIZE +# define ASF_PPI_SEQUENCE_FIELD_SIZE 0 +#endif + +#if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 1 +#endif +#if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 2 +#endif +#if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 4 +#endif +#ifndef ASF_PPI_PACKET_LENGTH_FIELD_SIZE +# define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 0 +#endif + +#if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 1 +#endif +#if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 2 +#endif +#if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) +# define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 4 +#endif +#ifndef ASF_PPI_PADDING_LENGTH_FIELD_SIZE +# define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 0 +#endif + +#if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 1 +#endif +#if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 2 +#endif +#if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 4 +#endif +#ifndef ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE +# define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 0 +#endif + +#if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 1 +#endif +#if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 2 +#endif +#if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 4 +#endif +#ifndef ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE +# define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 0 +#endif + +#if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 1 +#endif +#if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 2 +#endif +#if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 4 +#endif +#ifndef ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE +# define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 0 +#endif + +#if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_BYTE == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_LENGTH_FIELD_SIZE 1 +#endif +#if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) +# define ASF_PAYLOAD_LENGTH_FIELD_SIZE 2 +#endif +#ifndef ASF_PAYLOAD_LENGTH_FIELD_SIZE +# define ASF_PAYLOAD_LENGTH_FIELD_SIZE 0 +#endif + +#define PACKET_HEADER_MIN_SIZE \ + (ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE + \ + ASF_PACKET_ERROR_CORRECTION_DATA_SIZE + \ + 1 + /* Length Type Flags */ \ + 1 + /* Property Flags */ \ + ASF_PPI_PACKET_LENGTH_FIELD_SIZE + \ + ASF_PPI_SEQUENCE_FIELD_SIZE + \ + ASF_PPI_PADDING_LENGTH_FIELD_SIZE + \ + 4 + /* Send Time Field */ \ + 2) /* Duration Field */ + +// Replicated Data shall be at least 8 bytes long. +#define ASF_PAYLOAD_REPLICATED_DATA_LENGTH 0x08 + +#define PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD \ + (1 + /* Stream Number */ \ + ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ + ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ + ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ + ASF_PAYLOAD_REPLICATED_DATA_LENGTH) + +#define PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS \ + (1 + /* Stream Number */ \ + ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ + ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ + ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ + ASF_PAYLOAD_REPLICATED_DATA_LENGTH + \ + ASF_PAYLOAD_LENGTH_FIELD_SIZE) + +#define SINGLE_PAYLOAD_DATA_LENGTH \ + (PACKET_SIZE - \ + PACKET_HEADER_MIN_SIZE - \ + PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD) + +#define MULTI_PAYLOAD_CONSTANT \ + (PACKET_SIZE - \ + PACKET_HEADER_MIN_SIZE - \ + 1 - /* Payload Flags */ \ + 2 * PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS) + +typedef struct { + uint32_t seqno; + int is_streamed; + ASFStream streams[128]; ///< it's max number and it's not that big + /* non streamed additonnal info */ + uint64_t nb_packets; ///< how many packets are there in the file, invalid if broadcasting + int64_t duration; ///< in 100ns units + /* packet filling */ + unsigned char multi_payloads_present; + int packet_size_left; + int64_t packet_timestamp_start; + int64_t packet_timestamp_end; + unsigned int packet_nb_payloads; + uint8_t packet_buf[PACKET_SIZE]; + AVIOContext pb; + /* only for reading */ + uint64_t data_offset; ///< beginning of the first data packet + + ASFIndex *index_ptr; + uint32_t nb_index_memory_alloc; + uint16_t maximum_packet; + uint32_t next_packet_number; + uint16_t next_packet_count; + int next_start_sec; + int end_sec; +} ASFContext; + +static const AVCodecTag codec_asf_bmp_tags[] = { + { AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') }, + { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, + { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +#define PREROLL_TIME 3100 + +void ff_put_guid(AVIOContext *s, const ff_asf_guid *g) +{ + av_assert0(sizeof(*g) == 16); + avio_write(s, *g, sizeof(*g)); +} + +static void put_str16(AVIOContext *s, const char *tag) +{ + int len; + uint8_t *pb; + AVIOContext *dyn_buf; + if (avio_open_dyn_buf(&dyn_buf) < 0) + return; + + avio_put_str16le(dyn_buf, tag); + len = avio_close_dyn_buf(dyn_buf, &pb); + avio_wl16(s, len); + avio_write(s, pb, len); + av_freep(&pb); +} + +static int64_t put_header(AVIOContext *pb, const ff_asf_guid *g) +{ + int64_t pos; + + pos = avio_tell(pb); + ff_put_guid(pb, g); + avio_wl64(pb, 24); + return pos; +} + +/* update header size */ +static void end_header(AVIOContext *pb, int64_t pos) +{ + int64_t pos1; + + pos1 = avio_tell(pb); + avio_seek(pb, pos + 16, SEEK_SET); + avio_wl64(pb, pos1 - pos); + avio_seek(pb, pos1, SEEK_SET); +} + +/* write an asf chunk (only used in streaming case) */ +static void put_chunk(AVFormatContext *s, int type, + int payload_length, int flags) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + int length; + + length = payload_length + 8; + avio_wl16(pb, type); + avio_wl16(pb, length); // size + avio_wl32(pb, asf->seqno); // sequence number + avio_wl16(pb, flags); // unknown bytes + avio_wl16(pb, length); // size_confirm + asf->seqno++; +} + +/* convert from unix to windows time */ +static int64_t unix_to_file_time(int ti) +{ + int64_t t; + + t = ti * INT64_C(10000000); + t += INT64_C(116444736000000000); + return t; +} + +/* write the header (used two times if non streamed) */ +static int asf_write_header1(AVFormatContext *s, int64_t file_size, + int64_t data_chunk_size) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + AVDictionaryEntry *tags[5]; + int header_size, n, extra_size, extra_size2, wav_extra_size, file_time; + int has_title; + int metadata_count; + AVCodecContext *enc; + int64_t header_offset, cur_pos, hpos; + int bit_rate; + int64_t duration; + + ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL); + + tags[0] = av_dict_get(s->metadata, "title", NULL, 0); + tags[1] = av_dict_get(s->metadata, "author", NULL, 0); + tags[2] = av_dict_get(s->metadata, "copyright", NULL, 0); + tags[3] = av_dict_get(s->metadata, "comment", NULL, 0); + tags[4] = av_dict_get(s->metadata, "rating", NULL, 0); + + duration = asf->duration + PREROLL_TIME * 10000; + has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4]; + metadata_count = av_dict_count(s->metadata); + + bit_rate = 0; + for (n = 0; n < s->nb_streams; n++) { + enc = s->streams[n]->codec; + + avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */ + + bit_rate += enc->bit_rate; + } + + if (asf->is_streamed) { + put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ + } + + ff_put_guid(pb, &ff_asf_header); + avio_wl64(pb, -1); /* header length, will be patched after */ + avio_wl32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */ + avio_w8(pb, 1); /* ??? */ + avio_w8(pb, 2); /* ??? */ + + /* file header */ + header_offset = avio_tell(pb); + hpos = put_header(pb, &ff_asf_file_header); + ff_put_guid(pb, &ff_asf_my_guid); + avio_wl64(pb, file_size); + file_time = 0; + avio_wl64(pb, unix_to_file_time(file_time)); + avio_wl64(pb, asf->nb_packets); /* number of packets */ + avio_wl64(pb, duration); /* end time stamp (in 100ns units) */ + avio_wl64(pb, asf->duration); /* duration (in 100ns units) */ + avio_wl64(pb, PREROLL_TIME); /* start time stamp */ + avio_wl32(pb, (asf->is_streamed || !pb->seekable) ? 3 : 2); /* ??? */ + avio_wl32(pb, s->packet_size); /* packet size */ + avio_wl32(pb, s->packet_size); /* packet size */ + avio_wl32(pb, bit_rate); /* Nominal data rate in bps */ + end_header(pb, hpos); + + /* unknown headers */ + hpos = put_header(pb, &ff_asf_head1_guid); + ff_put_guid(pb, &ff_asf_head2_guid); + avio_wl32(pb, 6); + avio_wl16(pb, 0); + end_header(pb, hpos); + + /* title and other infos */ + if (has_title) { + int len; + uint8_t *buf; + AVIOContext *dyn_buf; + + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + hpos = put_header(pb, &ff_asf_comment_header); + + for (n = 0; n < FF_ARRAY_ELEMS(tags); n++) { + len = tags[n] ? avio_put_str16le(dyn_buf, tags[n]->value) : 0; + avio_wl16(pb, len); + } + len = avio_close_dyn_buf(dyn_buf, &buf); + avio_write(pb, buf, len); + av_freep(&buf); + end_header(pb, hpos); + } + if (metadata_count) { + AVDictionaryEntry *tag = NULL; + hpos = put_header(pb, &ff_asf_extended_content_header); + avio_wl16(pb, metadata_count); + while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + put_str16(pb, tag->key); + avio_wl16(pb, 0); + put_str16(pb, tag->value); + } + end_header(pb, hpos); + } + + /* stream headers */ + for (n = 0; n < s->nb_streams; n++) { + int64_t es_pos; + // ASFStream *stream = &asf->streams[n]; + + enc = s->streams[n]->codec; + asf->streams[n].num = n + 1; + asf->streams[n].seq = 1; + + switch (enc->codec_type) { + case AVMEDIA_TYPE_AUDIO: + wav_extra_size = 0; + extra_size = 18 + wav_extra_size; + extra_size2 = 8; + break; + default: + case AVMEDIA_TYPE_VIDEO: + wav_extra_size = enc->extradata_size; + extra_size = 0x33 + wav_extra_size; + extra_size2 = 0; + break; + } + + hpos = put_header(pb, &ff_asf_stream_header); + if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { + ff_put_guid(pb, &ff_asf_audio_stream); + ff_put_guid(pb, &ff_asf_audio_conceal_spread); + } else { + ff_put_guid(pb, &ff_asf_video_stream); + ff_put_guid(pb, &ff_asf_video_conceal_none); + } + avio_wl64(pb, 0); /* ??? */ + es_pos = avio_tell(pb); + avio_wl32(pb, extra_size); /* wav header len */ + avio_wl32(pb, extra_size2); /* additional data len */ + avio_wl16(pb, n + 1); /* stream number */ + avio_wl32(pb, 0); /* ??? */ + + if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { + /* WAVEFORMATEX header */ + int wavsize = ff_put_wav_header(pb, enc); + + if (wavsize < 0) + return -1; + if (wavsize != extra_size) { + cur_pos = avio_tell(pb); + avio_seek(pb, es_pos, SEEK_SET); + avio_wl32(pb, wavsize); /* wav header len */ + avio_seek(pb, cur_pos, SEEK_SET); + } + /* ERROR Correction */ + avio_w8(pb, 0x01); + if (enc->codec_id == AV_CODEC_ID_ADPCM_G726 || !enc->block_align) { + avio_wl16(pb, 0x0190); + avio_wl16(pb, 0x0190); + } else { + avio_wl16(pb, enc->block_align); + avio_wl16(pb, enc->block_align); + } + avio_wl16(pb, 0x01); + avio_w8(pb, 0x00); + } else { + avio_wl32(pb, enc->width); + avio_wl32(pb, enc->height); + avio_w8(pb, 2); /* ??? */ + avio_wl16(pb, 40 + enc->extradata_size); /* size */ + + /* BITMAPINFOHEADER header */ + ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1); + } + end_header(pb, hpos); + } + + /* media comments */ + + hpos = put_header(pb, &ff_asf_codec_comment_header); + ff_put_guid(pb, &ff_asf_codec_comment1_header); + avio_wl32(pb, s->nb_streams); + for (n = 0; n < s->nb_streams; n++) { + AVCodec *p; + const char *desc; + int len; + uint8_t *buf; + AVIOContext *dyn_buf; + + enc = s->streams[n]->codec; + p = avcodec_find_encoder(enc->codec_id); + + if (enc->codec_type == AVMEDIA_TYPE_AUDIO) + avio_wl16(pb, 2); + else if (enc->codec_type == AVMEDIA_TYPE_VIDEO) + avio_wl16(pb, 1); + else + avio_wl16(pb, -1); + + if (enc->codec_id == AV_CODEC_ID_WMAV2) + desc = "Windows Media Audio V8"; + else + desc = p ? p->name : enc->codec_name; + + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + avio_put_str16le(dyn_buf, desc); + len = avio_close_dyn_buf(dyn_buf, &buf); + avio_wl16(pb, len / 2); // "number of characters" = length in bytes / 2 + + avio_write(pb, buf, len); + av_freep(&buf); + + avio_wl16(pb, 0); /* no parameters */ + + /* id */ + if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { + avio_wl16(pb, 2); + avio_wl16(pb, enc->codec_tag); + } else { + avio_wl16(pb, 4); + avio_wl32(pb, enc->codec_tag); + } + if (!enc->codec_tag) + return -1; + } + end_header(pb, hpos); + + /* patch the header size fields */ + + cur_pos = avio_tell(pb); + header_size = cur_pos - header_offset; + if (asf->is_streamed) { + header_size += 8 + 30 + 50; + + avio_seek(pb, header_offset - 10 - 30, SEEK_SET); + avio_wl16(pb, header_size); + avio_seek(pb, header_offset - 2 - 30, SEEK_SET); + avio_wl16(pb, header_size); + + header_size -= 8 + 30 + 50; + } + header_size += 24 + 6; + avio_seek(pb, header_offset - 14, SEEK_SET); + avio_wl64(pb, header_size); + avio_seek(pb, cur_pos, SEEK_SET); + + /* movie chunk, followed by packets of packet_size */ + asf->data_offset = cur_pos; + ff_put_guid(pb, &ff_asf_data_header); + avio_wl64(pb, data_chunk_size); + ff_put_guid(pb, &ff_asf_my_guid); + avio_wl64(pb, asf->nb_packets); /* nb packets */ + avio_w8(pb, 1); /* ??? */ + avio_w8(pb, 1); /* ??? */ + return 0; +} + +static int asf_write_header(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + + s->packet_size = PACKET_SIZE; + asf->nb_packets = 0; + + asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK); + asf->nb_index_memory_alloc = ASF_INDEX_BLOCK; + asf->maximum_packet = 0; + + /* the data-chunk-size has to be 50, which is data_size - asf->data_offset + * at the moment this function is done. It is needed to use asf as + * streamable format. */ + if (asf_write_header1(s, 0, 50) < 0) { + //av_free(asf); + return -1; + } + + avio_flush(s->pb); + + asf->packet_nb_payloads = 0; + asf->packet_timestamp_start = -1; + asf->packet_timestamp_end = -1; + ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, + NULL, NULL, NULL, NULL); + + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + + return 0; +} + +static int asf_write_stream_header(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + + asf->is_streamed = 1; + + return asf_write_header(s); +} + +static int put_payload_parsing_info(AVFormatContext *s, + unsigned sendtime, unsigned duration, + int nb_payloads, int padsize) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = s->pb; + int ppi_size, i; + int64_t start = avio_tell(pb); + + int iLengthTypeFlags = ASF_PPI_LENGTH_TYPE_FLAGS; + + padsize -= PACKET_HEADER_MIN_SIZE; + if (asf->multi_payloads_present) + padsize--; + av_assert0(padsize >= 0); + + avio_w8(pb, ASF_PACKET_ERROR_CORRECTION_FLAGS); + for (i = 0; i < ASF_PACKET_ERROR_CORRECTION_DATA_SIZE; i++) + avio_w8(pb, 0x0); + + if (asf->multi_payloads_present) + iLengthTypeFlags |= ASF_PPI_FLAG_MULTIPLE_PAYLOADS_PRESENT; + + if (padsize > 0) { + if (padsize < 256) + iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE; + else + iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD; + } + avio_w8(pb, iLengthTypeFlags); + + avio_w8(pb, ASF_PPI_PROPERTY_FLAGS); + + if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD) + avio_wl16(pb, padsize - 2); + if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE) + avio_w8(pb, padsize - 1); + + avio_wl32(pb, sendtime); + avio_wl16(pb, duration); + if (asf->multi_payloads_present) + avio_w8(pb, nb_payloads | ASF_PAYLOAD_FLAGS); + + ppi_size = avio_tell(pb) - start; + + return ppi_size; +} + +static void flush_packet(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int packet_hdr_size, packet_filled_size; + + av_assert0(asf->packet_timestamp_end >= asf->packet_timestamp_start); + + if (asf->is_streamed) + put_chunk(s, 0x4424, s->packet_size, 0); + + packet_hdr_size = put_payload_parsing_info(s, + asf->packet_timestamp_start, + asf->packet_timestamp_end - asf->packet_timestamp_start, + asf->packet_nb_payloads, + asf->packet_size_left); + + packet_filled_size = PACKET_SIZE - asf->packet_size_left; + av_assert0(packet_hdr_size <= asf->packet_size_left); + memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left); + + avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size); + + avio_flush(s->pb); + asf->nb_packets++; + asf->packet_nb_payloads = 0; + asf->packet_timestamp_start = -1; + asf->packet_timestamp_end = -1; + ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, + NULL, NULL, NULL, NULL); +} + +static void put_payload_header(AVFormatContext *s, ASFStream *stream, + int64_t presentation_time, int m_obj_size, + int m_obj_offset, int payload_len, int flags) +{ + ASFContext *asf = s->priv_data; + AVIOContext *pb = &asf->pb; + int val; + + val = stream->num; + if (flags & AV_PKT_FLAG_KEY) + val |= ASF_PL_FLAG_KEY_FRAME; + avio_w8(pb, val); + + avio_w8(pb, stream->seq); // Media object number + avio_wl32(pb, m_obj_offset); // Offset Into Media Object + + // Replicated Data shall be at least 8 bytes long. + // The first 4 bytes of data shall contain the + // Size of the Media Object that the payload belongs to. + // The next 4 bytes of data shall contain the + // Presentation Time for the media object that the payload belongs to. + avio_w8(pb, ASF_PAYLOAD_REPLICATED_DATA_LENGTH); + + avio_wl32(pb, m_obj_size); // Replicated Data - Media Object Size + avio_wl32(pb, (uint32_t) presentation_time); // Replicated Data - Presentation Time + + if (asf->multi_payloads_present) { + avio_wl16(pb, payload_len); // payload length + } +} + +static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, + int64_t timestamp, const uint8_t *buf, + int m_obj_size, int flags) +{ + ASFContext *asf = s->priv_data; + int m_obj_offset, payload_len, frag_len1; + + m_obj_offset = 0; + while (m_obj_offset < m_obj_size) { + payload_len = m_obj_size - m_obj_offset; + if (asf->packet_timestamp_start == -1) { + asf->multi_payloads_present = (payload_len < MULTI_PAYLOAD_CONSTANT); + + asf->packet_size_left = PACKET_SIZE; + if (asf->multi_payloads_present) { + frag_len1 = MULTI_PAYLOAD_CONSTANT - 1; + } else { + frag_len1 = SINGLE_PAYLOAD_DATA_LENGTH; + } + asf->packet_timestamp_start = timestamp; + } else { + // multi payloads + frag_len1 = asf->packet_size_left - + PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS - + PACKET_HEADER_MIN_SIZE - 1; + + if (frag_len1 < payload_len && + avst->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + flush_packet(s); + continue; + } + } + if (frag_len1 > 0) { + if (payload_len > frag_len1) + payload_len = frag_len1; + else if (payload_len == (frag_len1 - 1)) + payload_len = frag_len1 - 2; // additional byte need to put padding length + + put_payload_header(s, stream, timestamp + PREROLL_TIME, + m_obj_size, m_obj_offset, payload_len, flags); + avio_write(&asf->pb, buf, payload_len); + + if (asf->multi_payloads_present) + asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS); + else + asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD); + asf->packet_timestamp_end = timestamp; + + asf->packet_nb_payloads++; + } else { + payload_len = 0; + } + m_obj_offset += payload_len; + buf += payload_len; + + if (!asf->multi_payloads_present) + flush_packet(s); + else if (asf->packet_size_left <= (PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS + PACKET_HEADER_MIN_SIZE + 1)) + flush_packet(s); + } + stream->seq++; +} + +static void update_index(AVFormatContext *s, int start_sec, + uint32_t packet_number, uint16_t packet_count) +{ + ASFContext *asf = s->priv_data; + + if (start_sec > asf->next_start_sec) { + int i; + + if (!asf->next_start_sec) { + asf->next_packet_number = packet_number; + asf->next_packet_count = packet_count; + } + + if (start_sec > asf->nb_index_memory_alloc) { + asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1); + asf->index_ptr = av_realloc( asf->index_ptr, sizeof(ASFIndex) * asf->nb_index_memory_alloc ); + } + for (i = asf->next_start_sec; i < start_sec; i++) { + asf->index_ptr[i].packet_number = asf->next_packet_number; + asf->index_ptr[i].packet_count = asf->next_packet_count; + } + } + asf->maximum_packet = FFMAX(asf->maximum_packet, packet_count); + asf->next_packet_number = packet_number; + asf->next_packet_count = packet_count; + asf->next_start_sec = start_sec; +} + +static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASFContext *asf = s->priv_data; + ASFStream *stream; + AVCodecContext *codec; + uint32_t packet_number; + int64_t pts; + int start_sec; + int flags = pkt->flags; + + codec = s->streams[pkt->stream_index]->codec; + stream = &asf->streams[pkt->stream_index]; + + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) + flags &= ~AV_PKT_FLAG_KEY; + + pts = (pkt->pts != AV_NOPTS_VALUE) ? pkt->pts : pkt->dts; + av_assert0(pts != AV_NOPTS_VALUE); + pts *= 10000; + asf->duration = FFMAX(asf->duration, pts + pkt->duration * 10000); + + packet_number = asf->nb_packets; + put_frame(s, stream, s->streams[pkt->stream_index], + pkt->dts, pkt->data, pkt->size, flags); + + start_sec = (int)((PREROLL_TIME * 10000 + pts + ASF_INDEXED_INTERVAL - 1) + / ASF_INDEXED_INTERVAL); + + /* check index */ + if ((!asf->is_streamed) && (flags & AV_PKT_FLAG_KEY)) { + uint16_t packet_count = asf->nb_packets - packet_number; + update_index(s, start_sec, packet_number, packet_count); + } + asf->end_sec = start_sec; + + return 0; +} + +static int asf_write_index(AVFormatContext *s, ASFIndex *index, + uint16_t max, uint32_t count) +{ + AVIOContext *pb = s->pb; + int i; + + ff_put_guid(pb, &ff_asf_simple_index_header); + avio_wl64(pb, 24 + 16 + 8 + 4 + 4 + (4 + 2) * count); + ff_put_guid(pb, &ff_asf_my_guid); + avio_wl64(pb, ASF_INDEXED_INTERVAL); + avio_wl32(pb, max); + avio_wl32(pb, count); + for (i = 0; i < count; i++) { + avio_wl32(pb, index[i].packet_number); + avio_wl16(pb, index[i].packet_count); + } + + return 0; +} + +static int asf_write_trailer(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int64_t file_size, data_size; + + /* flush the current packet */ + if (asf->pb.buf_ptr > asf->pb.buffer) + flush_packet(s); + + /* write index */ + data_size = avio_tell(s->pb); + if (!asf->is_streamed && asf->next_start_sec) { + update_index(s, asf->end_sec + 1, 0, 0); + asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); + } + avio_flush(s->pb); + + if (asf->is_streamed || !s->pb->seekable) { + put_chunk(s, 0x4524, 0, 0); /* end of stream */ + } else { + /* rewrite an updated header */ + file_size = avio_tell(s->pb); + avio_seek(s->pb, 0, SEEK_SET); + asf_write_header1(s, file_size, data_size - asf->data_offset); + } + + av_free(asf->index_ptr); + return 0; +} + +#if CONFIG_ASF_MUXER +AVOutputFormat ff_asf_muxer = { + .name = "asf", + .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .mime_type = "video/x-ms-asf", + .extensions = "asf,wmv,wma", + .priv_data_size = sizeof(ASFContext), + .audio_codec = AV_CODEC_ID_WMAV2, + .video_codec = AV_CODEC_ID_MSMPEG4V3, + .write_header = asf_write_header, + .write_packet = asf_write_packet, + .write_trailer = asf_write_trailer, + .flags = AVFMT_GLOBALHEADER, + .codec_tag = (const AVCodecTag * const []) { + codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0 + }, +}; +#endif /* CONFIG_ASF_MUXER */ + +#if CONFIG_ASF_STREAM_MUXER +AVOutputFormat ff_asf_stream_muxer = { + .name = "asf_stream", + .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .mime_type = "video/x-ms-asf", + .extensions = "asf,wmv,wma", + .priv_data_size = sizeof(ASFContext), + .audio_codec = AV_CODEC_ID_WMAV2, + .video_codec = AV_CODEC_ID_MSMPEG4V3, + .write_header = asf_write_stream_header, + .write_packet = asf_write_packet, + .write_trailer = asf_write_trailer, + .flags = AVFMT_GLOBALHEADER, + .codec_tag = (const AVCodecTag * const []) { + codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0 + }, +}; +#endif /* CONFIG_ASF_STREAM_MUXER */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/asfenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/asfenc.o libavformat/asfenc.o: libavformat/asfenc.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/dict.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h libavformat/riff.h \ + libavformat/metadata.h libavformat/asf.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/asfenc.o Binary file ffmpeg/libavformat/asfenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/assdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,169 @@ +/* + * SSA/ASS demuxer + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavutil/bprint.h" + +typedef struct ASSContext{ + FFDemuxSubtitlesQueue q; +}ASSContext; + +static int ass_probe(AVProbeData *p) +{ + const char *header= "[Script Info]"; + + if( !memcmp(p->buf , header, strlen(header)) + || !memcmp(p->buf+3, header, strlen(header))) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int ass_read_close(AVFormatContext *s) +{ + ASSContext *ass = s->priv_data; + ff_subtitles_queue_clean(&ass->q); + return 0; +} + +static int read_ts(const uint8_t *p, int64_t *start, int *duration) +{ + int64_t end; + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + + if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d", + &hh1, &mm1, &ss1, &ms1, + &hh2, &mm2, &ss2, &ms2) == 8) { + end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2; + *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1; + *duration = end - *start; + return 0; + } + return -1; +} + +static int64_t get_line(AVBPrint *buf, AVIOContext *pb) +{ + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); + for (;;) { + char c = avio_r8(pb); + if (!c) + break; + av_bprint_chars(buf, c, 1); + if (c == '\n') + break; + } + return pos; +} + +static int ass_read_header(AVFormatContext *s) +{ + ASSContext *ass = s->priv_data; + AVBPrint header, line; + int header_remaining, res = 0; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id= AV_CODEC_ID_SSA; + + header_remaining= INT_MAX; + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED); + + for (;;) { + int64_t pos = get_line(&line, s->pb); + + if (!line.str[0]) // EOF + break; + + if (!memcmp(line.str, "[Events]", 8)) + header_remaining= 2; + else if (line.str[0]=='[') + header_remaining= INT_MAX; + + if (header_remaining) { + av_bprintf(&header, "%s", line.str); + header_remaining--; + } else { + int64_t ts_start = AV_NOPTS_VALUE; + int duration = -1; + AVPacket *sub; + + if (read_ts(line.str, &ts_start, &duration) < 0) + continue; + sub = ff_subtitles_queue_insert(&ass->q, line.str, line.len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = ts_start; + sub->duration = duration; + } + } + + av_bprint_finalize(&line, NULL); + + res = avpriv_bprint_to_extradata(st->codec, &header); + if (res < 0) + goto end; + + ff_subtitles_queue_finalize(&ass->q); + +end: + return res; +} + +static int ass_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASSContext *ass = s->priv_data; + return ff_subtitles_queue_read_packet(&ass->q, pkt); +} + +static int ass_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ASSContext *ass = s->priv_data; + return ff_subtitles_queue_seek(&ass->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +AVInputFormat ff_ass_demuxer = { + .name = "ass", + .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), + .priv_data_size = sizeof(ASSContext), + .read_probe = ass_probe, + .read_header = ass_read_header, + .read_packet = ass_read_packet, + .read_close = ass_read_close, + .read_seek2 = ass_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/assdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/assdec.o libavformat/assdec.o: libavformat/assdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h \ + libavcodec/internal.h libavutil/mathematics.h libavcodec/avcodec.h \ + config.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assdec.o Binary file ffmpeg/libavformat/assdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/assenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,115 @@ +/* + * SSA/ASS muxer + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +typedef struct ASSContext{ + unsigned int extra_index; + int write_ts; // 0: ssa (timing in payload), 1: ass (matroska like) +}ASSContext; + +static int write_header(AVFormatContext *s) +{ + ASSContext *ass = s->priv_data; + AVCodecContext *avctx= s->streams[0]->codec; + uint8_t *last= NULL; + + if (s->nb_streams != 1 || (avctx->codec_id != AV_CODEC_ID_SSA && + avctx->codec_id != AV_CODEC_ID_ASS)) { + av_log(s, AV_LOG_ERROR, "Exactly one ASS/SSA stream is needed.\n"); + return -1; + } + ass->write_ts = avctx->codec_id == AV_CODEC_ID_ASS; + avpriv_set_pts_info(s->streams[0], 64, 1, 100); + + while(ass->extra_index < avctx->extradata_size){ + uint8_t *p = avctx->extradata + ass->extra_index; + uint8_t *end= strchr(p, '\n'); + if(!end) end= avctx->extradata + avctx->extradata_size; + else end++; + + avio_write(s->pb, p, end-p); + ass->extra_index += end-p; + + if(last && !memcmp(last, "[Events]", 8)) + break; + last=p; + } + + avio_flush(s->pb); + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASSContext *ass = s->priv_data; + + if (ass->write_ts) { + long int layer; + char *p; + int64_t start = pkt->pts; + int64_t end = start + pkt->duration; + int hh1, mm1, ss1, ms1; + int hh2, mm2, ss2, ms2; + + p = pkt->data + strcspn(pkt->data, ",") + 1; // skip ReadOrder + layer = strtol(p, &p, 10); + if (*p == ',') + p++; + hh1 = (int)(start / 360000); mm1 = (int)(start / 6000) % 60; + hh2 = (int)(end / 360000); mm2 = (int)(end / 6000) % 60; + ss1 = (int)(start / 100) % 60; ms1 = (int)(start % 100); + ss2 = (int)(end / 100) % 60; ms2 = (int)(end % 100); + if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99; + if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99; + avio_printf(s->pb, "Dialogue: %ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s\r\n", + layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, p); + } else { + avio_write(s->pb, pkt->data, pkt->size); + } + return 0; +} + +static int write_trailer(AVFormatContext *s) +{ + ASSContext *ass = s->priv_data; + AVCodecContext *avctx= s->streams[0]->codec; + + avio_write(s->pb, avctx->extradata + ass->extra_index, + avctx->extradata_size - ass->extra_index); + + return 0; +} + +AVOutputFormat ff_ass_muxer = { + .name = "ass", + .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), + .mime_type = "text/x-ssa", + .extensions = "ass,ssa", + .priv_data_size = sizeof(ASSContext), + .subtitle_codec = AV_CODEC_ID_SSA, + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/assenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/assenc.o libavformat/assenc.o: libavformat/assenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/assenc.o Binary file ffmpeg/libavformat/assenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ast.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ast.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,29 @@ +/* + * AST common code + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +const AVCodecTag ff_codec_ast_tags[] = { + { AV_CODEC_ID_ADPCM_AFC, 0 }, + { AV_CODEC_ID_PCM_S16BE_PLANAR, 1 }, + { AV_CODEC_ID_NONE, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ast.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ast.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/ast.o libavformat/ast.o: libavformat/ast.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ast.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ast.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,30 @@ +/* + * AST common code + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AST_H +#define AVFORMAT_AST_H + +#include "avformat.h" +#include "internal.h" + +extern const AVCodecTag ff_codec_ast_tags[]; + +#endif /* AVFORMAT_AST_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ast.o Binary file ffmpeg/libavformat/ast.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/astdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,119 @@ +/* + * AST demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "ast.h" + +static int ast_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('S','T','R','M') && + AV_RB16(p->buf + 10) && + AV_RB16(p->buf + 12) && + AV_RB32(p->buf + 16)) + return AVPROBE_SCORE_MAX / 3 * 2; + return 0; +} + +static int ast_read_header(AVFormatContext *s) +{ + int depth; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(s->pb, 8); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = ff_codec_get_id(ff_codec_ast_tags, avio_rb16(s->pb)); + + depth = avio_rb16(s->pb); + if (depth != 16) { + avpriv_request_sample(s, "depth %d", depth); + return AVERROR_INVALIDDATA; + } + + st->codec->channels = avio_rb16(s->pb); + if (!st->codec->channels) + return AVERROR_INVALIDDATA; + + if (st->codec->channels == 2) + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + else if (st->codec->channels == 4) + st->codec->channel_layout = AV_CH_LAYOUT_4POINT0; + + avio_skip(s->pb, 2); + st->codec->sample_rate = avio_rb32(s->pb); + if (st->codec->sample_rate <= 0) + return AVERROR_INVALIDDATA; + st->start_time = 0; + st->duration = avio_rb32(s->pb); + avio_skip(s->pb, 40); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +static int ast_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + uint32_t type, size; + int64_t pos; + int ret; + + if (url_feof(s->pb)) + return AVERROR_EOF; + + pos = avio_tell(s->pb); + type = avio_rl32(s->pb); + size = avio_rb32(s->pb); + if (size > INT_MAX / s->streams[0]->codec->channels) + return AVERROR_INVALIDDATA; + + size *= s->streams[0]->codec->channels; + if ((ret = avio_skip(s->pb, 24)) < 0) // padding + return ret; + + if (type == MKTAG('B','L','C','K')) { + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 0; + pkt->pos = pos; + } else { + av_log(s, AV_LOG_ERROR, "unknown chunk %x\n", type); + avio_skip(s->pb, size); + ret = AVERROR_INVALIDDATA; + } + + return ret; +} + +AVInputFormat ff_ast_demuxer = { + .name = "ast", + .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .read_probe = ast_probe, + .read_header = ast_read_header, + .read_packet = ast_read_packet, + .extensions = "ast", + .flags = AVFMT_GENERIC_INDEX, + .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/astdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/astdec.o libavformat/astdec.o: libavformat/astdec.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/ast.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astdec.o Binary file ffmpeg/libavformat/astdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/astenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,214 @@ +/* + * AST muxer + * Copyright (c) 2012 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "ast.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" + +typedef struct ASTMuxContext { + AVClass *class; + int64_t size; + int64_t samples; + int64_t loopstart; + int64_t loopend; + int fbs; +} ASTMuxContext; + +#define CHECK_LOOP(type) \ + if (ast->loop ## type > 0) { \ + ast->loop ## type = av_rescale_rnd(ast->loop ## type, enc->sample_rate, 1000, AV_ROUND_DOWN); \ + if (ast->loop ## type < 0 || ast->loop ## type > UINT_MAX) { \ + av_log(s, AV_LOG_ERROR, "Invalid loop" #type " value\n"); \ + return AVERROR(EINVAL); \ + } \ + } + +static int ast_write_header(AVFormatContext *s) +{ + ASTMuxContext *ast = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc; + unsigned int codec_tag; + + if (s->nb_streams == 1) { + enc = s->streams[0]->codec; + } else { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + if (enc->codec_id == AV_CODEC_ID_ADPCM_AFC) { + av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n"); + return AVERROR_PATCHWELCOME; + } + + codec_tag = ff_codec_get_tag(ff_codec_ast_tags, enc->codec_id); + if (!codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + if (ast->loopend > 0 && ast->loopstart >= ast->loopend) { + av_log(s, AV_LOG_ERROR, "loopend can't be less or equal to loopstart\n"); + return AVERROR(EINVAL); + } + + /* Convert milliseconds to samples */ + CHECK_LOOP(start) + CHECK_LOOP(end) + + ffio_wfourcc(pb, "STRM"); + + ast->size = avio_tell(pb); + avio_wb32(pb, 0); /* File size minus header */ + avio_wb16(pb, codec_tag); + avio_wb16(pb, 16); /* Bit depth */ + avio_wb16(pb, enc->channels); + avio_wb16(pb, 0); /* Loop flag */ + avio_wb32(pb, enc->sample_rate); + + ast->samples = avio_tell(pb); + avio_wb32(pb, 0); /* Number of samples */ + avio_wb32(pb, 0); /* Loopstart */ + avio_wb32(pb, 0); /* Loopend */ + avio_wb32(pb, 0); /* Size of first block */ + + /* Unknown */ + avio_wb32(pb, 0); + avio_wl32(pb, 0x7F); + avio_wb64(pb, 0); + avio_wb64(pb, 0); + avio_wb32(pb, 0); + + avio_flush(pb); + + return 0; +} + +static int ast_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + ASTMuxContext *ast = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + int size = pkt->size / enc->channels; + + if (enc->frame_number == 1) + ast->fbs = size; + + ffio_wfourcc(pb, "BLCK"); + avio_wb32(pb, size); /* Block size */ + + /* padding */ + avio_wb64(pb, 0); + avio_wb64(pb, 0); + avio_wb64(pb, 0); + + avio_write(pb, pkt->data, pkt->size); + + return 0; +} + +static int ast_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + ASTMuxContext *ast = s->priv_data; + AVCodecContext *enc = s->streams[0]->codec; + int64_t file_size = avio_tell(pb); + int64_t samples = (file_size - 64 - (32 * enc->frame_number)) / enc->block_align; /* PCM_S16BE_PLANAR */ + + av_log(s, AV_LOG_DEBUG, "total samples: %"PRId64"\n", samples); + + if (s->pb->seekable) { + /* Number of samples */ + avio_seek(pb, ast->samples, SEEK_SET); + avio_wb32(pb, samples); + + /* Loopstart if provided */ + if (ast->loopstart > 0) { + if (ast->loopstart >= samples) { + av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n"); + ast->loopstart = -1; + avio_skip(pb, 4); + } else + avio_wb32(pb, ast->loopstart); + } else + avio_skip(pb, 4); + + /* Loopend if provided. Otherwise number of samples again */ + if (ast->loopend && ast->loopstart >= 0) { + if (ast->loopend > samples) { + av_log(s, AV_LOG_WARNING, "Loopend value is out of range and will be ignored\n"); + ast->loopend = samples; + } + avio_wb32(pb, ast->loopend); + } else { + avio_wb32(pb, samples); + } + + /* Size of first block */ + avio_wb32(pb, ast->fbs); + + /* File size minus header */ + avio_seek(pb, ast->size, SEEK_SET); + avio_wb32(pb, file_size - 64); + + /* Loop flag */ + if (ast->loopstart >= 0) { + avio_skip(pb, 6); + avio_wb16(pb, 0xFFFF); + } + + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } + return 0; +} + +#define OFFSET(obj) offsetof(ASTMuxContext, obj) +static const AVOption options[] = { + { "loopstart", "Loopstart position in milliseconds.", OFFSET(loopstart), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "loopend", "Loopend position in milliseconds.", OFFSET(loopend), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL }, +}; + +static const AVClass ast_muxer_class = { + .class_name = "AST muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_ast_muxer = { + .name = "ast", + .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .extensions = "ast", + .priv_data_size = sizeof(ASTMuxContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, + .video_codec = AV_CODEC_ID_NONE, + .write_header = ast_write_header, + .write_packet = ast_write_packet, + .write_trailer = ast_write_trailer, + .priv_class = &ast_muxer_class, + .codec_tag = (const AVCodecTag* const []){ff_codec_ast_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/astenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/astenc.o libavformat/astenc.o: libavformat/astenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h libavformat/ast.h \ + libavutil/mathematics.h libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/astenc.o Binary file ffmpeg/libavformat/astenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/au.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/au.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,211 @@ +/* + * AU muxer and demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * first version by Francois Revol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Reference documents: + * http://www.opengroup.org/public/pubs/external/auformat.html + * http://www.goice.co.jp/member/mo/formats/au.html + */ + +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "pcm.h" + +/* if we don't know the size in advance */ +#define AU_UNKNOWN_SIZE ((uint32_t)(~0)) +/* the specification requires an annotation field of at least eight bytes */ +#define AU_HEADER_SIZE (24+8) + +static const AVCodecTag codec_au_tags[] = { + { AV_CODEC_ID_PCM_MULAW, 1 }, + { AV_CODEC_ID_PCM_S8, 2 }, + { AV_CODEC_ID_PCM_S16BE, 3 }, + { AV_CODEC_ID_PCM_S24BE, 4 }, + { AV_CODEC_ID_PCM_S32BE, 5 }, + { AV_CODEC_ID_PCM_F32BE, 6 }, + { AV_CODEC_ID_PCM_F64BE, 7 }, + { AV_CODEC_ID_ADPCM_G722,24 }, + { AV_CODEC_ID_PCM_ALAW, 27 }, + { AV_CODEC_ID_NONE, 0 }, +}; + +#if CONFIG_AU_DEMUXER + +static int au_probe(AVProbeData *p) +{ + if (p->buf[0] == '.' && p->buf[1] == 's' && + p->buf[2] == 'n' && p->buf[3] == 'd') + return AVPROBE_SCORE_MAX; + else + return 0; +} + +#define BLOCK_SIZE 1024 + +static int au_read_header(AVFormatContext *s) +{ + int size, data_size = 0; + unsigned int tag; + AVIOContext *pb = s->pb; + unsigned int id, channels, rate; + int bps; + enum AVCodecID codec; + AVStream *st; + + tag = avio_rl32(pb); + if (tag != MKTAG('.', 's', 'n', 'd')) + return AVERROR_INVALIDDATA; + size = avio_rb32(pb); /* header size */ + data_size = avio_rb32(pb); /* data size in bytes */ + + if (data_size < 0 && data_size != AU_UNKNOWN_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid negative data size '%d' found\n", data_size); + return AVERROR_INVALIDDATA; + } + + id = avio_rb32(pb); + rate = avio_rb32(pb); + channels = avio_rb32(pb); + + if (size > 24) { + /* skip unused data */ + avio_skip(pb, size - 24); + } + + codec = ff_codec_get_id(codec_au_tags, id); + + if (codec == AV_CODEC_ID_NONE) { + avpriv_request_sample(s, "unknown or unsupported codec tag: %u", id); + return AVERROR_PATCHWELCOME; + } + + bps = av_get_bits_per_sample(codec); + if (!bps) { + avpriv_request_sample(s, "Unknown bits per sample"); + return AVERROR_PATCHWELCOME; + } + + if (channels == 0 || channels >= INT_MAX / (BLOCK_SIZE * bps >> 3)) { + av_log(s, AV_LOG_ERROR, "Invalid number of channels %u\n", channels); + return AVERROR_INVALIDDATA; + } + + if (rate == 0 || rate > INT_MAX) { + av_log(s, AV_LOG_ERROR, "Invalid sample rate: %u\n", rate); + return AVERROR_INVALIDDATA; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = id; + st->codec->codec_id = codec; + st->codec->channels = channels; + st->codec->sample_rate = rate; + st->codec->bit_rate = channels * rate * bps; + st->codec->block_align = FFMAX(bps * st->codec->channels / 8, 1); + if (data_size != AU_UNKNOWN_SIZE) + st->duration = (((int64_t)data_size)<<3) / (st->codec->channels * (int64_t)bps); + + st->start_time = 0; + avpriv_set_pts_info(st, 64, 1, rate); + + return 0; +} + +AVInputFormat ff_au_demuxer = { + .name = "au", + .long_name = NULL_IF_CONFIG_SMALL("Sun AU"), + .read_probe = au_probe, + .read_header = au_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .codec_tag = (const AVCodecTag* const []) { codec_au_tags, 0 }, +}; + +#endif /* CONFIG_AU_DEMUXER */ + +#if CONFIG_AU_MUXER + +#include "rawenc.h" + +static int au_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + enc->codec_tag = ff_codec_get_tag(codec_au_tags, enc->codec_id); + if (!enc->codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + ffio_wfourcc(pb, ".snd"); /* magic number */ + avio_wb32(pb, AU_HEADER_SIZE); /* header size */ + avio_wb32(pb, AU_UNKNOWN_SIZE); /* data size */ + avio_wb32(pb, enc->codec_tag); /* codec ID */ + avio_wb32(pb, enc->sample_rate); + avio_wb32(pb, enc->channels); + avio_wb64(pb, 0); /* annotation field */ + avio_flush(pb); + + return 0; +} + +static int au_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int64_t file_size = avio_tell(pb); + + if (s->pb->seekable && file_size < INT32_MAX) { + /* update file size */ + avio_seek(pb, 8, SEEK_SET); + avio_wb32(pb, (uint32_t)(file_size - AU_HEADER_SIZE)); + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } + + return 0; +} + +AVOutputFormat ff_au_muxer = { + .name = "au", + .long_name = NULL_IF_CONFIG_SMALL("Sun AU"), + .mime_type = "audio/basic", + .extensions = "au", + .audio_codec = AV_CODEC_ID_PCM_S16BE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = au_write_header, + .write_packet = ff_raw_write_packet, + .write_trailer = au_write_trailer, + .codec_tag = (const AVCodecTag* const []) { codec_au_tags, 0 }, +}; + +#endif /* CONFIG_AU_MUXER */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/au.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/au.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/au.o libavformat/au.o: libavformat/au.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/pcm.h \ + libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/au.o Binary file ffmpeg/libavformat/au.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/audiointerleave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/audiointerleave.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,148 @@ +/* + * Audio Interleaving functions + * + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/fifo.h" +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "audiointerleave.h" +#include "internal.h" + +void ff_audio_interleave_close(AVFormatContext *s) +{ + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AudioInterleaveContext *aic = st->priv_data; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + av_fifo_free(aic->fifo); + } +} + +int ff_audio_interleave_init(AVFormatContext *s, + const int *samples_per_frame, + AVRational time_base) +{ + int i; + + if (!samples_per_frame) + return -1; + + if (!time_base.num) { + av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n"); + return -1; + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AudioInterleaveContext *aic = st->priv_data; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + aic->sample_size = (st->codec->channels * + av_get_bits_per_sample(st->codec->codec_id)) / 8; + if (!aic->sample_size) { + av_log(s, AV_LOG_ERROR, "could not compute sample size\n"); + return -1; + } + aic->samples_per_frame = samples_per_frame; + aic->samples = aic->samples_per_frame; + aic->time_base = time_base; + + aic->fifo_size = 100* *aic->samples; + aic->fifo= av_fifo_alloc(100 * *aic->samples); + } + } + + return 0; +} + +static int ff_interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, + int stream_index, int flush) +{ + AVStream *st = s->streams[stream_index]; + AudioInterleaveContext *aic = st->priv_data; + + int size = FFMIN(av_fifo_size(aic->fifo), *aic->samples * aic->sample_size); + if (!size || (!flush && size == av_fifo_size(aic->fifo))) + return 0; + + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); + av_fifo_generic_read(aic->fifo, pkt->data, size, NULL); + + pkt->dts = pkt->pts = aic->dts; + pkt->duration = av_rescale_q(*aic->samples, st->time_base, aic->time_base); + pkt->stream_index = stream_index; + aic->dts += pkt->duration; + + aic->samples++; + if (!*aic->samples) + aic->samples = aic->samples_per_frame; + + return size; +} + +int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush, + int (*get_packet)(AVFormatContext *, AVPacket *, AVPacket *, int), + int (*compare_ts)(AVFormatContext *, AVPacket *, AVPacket *)) +{ + int i; + + if (pkt) { + AVStream *st = s->streams[pkt->stream_index]; + AudioInterleaveContext *aic = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + unsigned new_size = av_fifo_size(aic->fifo) + pkt->size; + if (new_size > aic->fifo_size) { + if (av_fifo_realloc2(aic->fifo, new_size) < 0) + return -1; + aic->fifo_size = new_size; + } + av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL); + } else { + int ret; + // rewrite pts and dts to be decoded time line position + pkt->pts = pkt->dts = aic->dts; + aic->dts += pkt->duration; + ret = ff_interleave_add_packet(s, pkt, compare_ts); + if (ret < 0) + return ret; + } + pkt = NULL; + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + AVPacket new_pkt; + int ret; + while ((ret = ff_interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) { + ret = ff_interleave_add_packet(s, &new_pkt, compare_ts); + if (ret < 0) + return ret; + } + if (ret < 0) + return ret; + } + } + + return get_packet(s, out, NULL, flush); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/audiointerleave.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/audiointerleave.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/audiointerleave.o libavformat/audiointerleave.o: \ + libavformat/audiointerleave.c libavutil/fifo.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/mathematics.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/audiointerleave.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/audiointerleave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/audiointerleave.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,55 @@ +/* + * audio interleaving prototypes and declarations + * + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AUDIOINTERLEAVE_H +#define AVFORMAT_AUDIOINTERLEAVE_H + +#include "libavutil/fifo.h" +#include "avformat.h" + +typedef struct AudioInterleaveContext { + AVFifoBuffer *fifo; + unsigned fifo_size; ///< size of currently allocated FIFO + uint64_t dts; ///< current dts + int sample_size; ///< size of one sample all channels included + const int *samples_per_frame; ///< must be 0-terminated + const int *samples; ///< current samples per frame, pointer to samples_per_frame + AVRational time_base; ///< time base of output audio packets +} AudioInterleaveContext; + +int ff_audio_interleave_init(AVFormatContext *s, const int *samples_per_frame, AVRational time_base); +void ff_audio_interleave_close(AVFormatContext *s); + +/** + * Rechunk audio PCM packets per AudioInterleaveContext->samples_per_frame + * and interleave them correctly. + * The first element of AVStream->priv_data must be AudioInterleaveContext + * when using this function. + * + * @param get_packet function will output a packet when streams are correctly interleaved. + * @param compare_ts function will compare AVPackets and decide interleaving order. + */ +int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush, + int (*get_packet)(AVFormatContext *, AVPacket *, AVPacket *, int), + int (*compare_ts)(AVFormatContext *, AVPacket *, AVPacket *)); + +#endif /* AVFORMAT_AUDIOINTERLEAVE_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/audiointerleave.o Binary file ffmpeg/libavformat/audiointerleave.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,193 @@ +/* + * AVC helper functions for muxers + * Copyright (c) 2006 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio.h" +#include "avc.h" + +static const uint8_t *ff_avc_find_startcode_internal(const uint8_t *p, const uint8_t *end) +{ + const uint8_t *a = p + 4 - ((intptr_t)p & 3); + + for (end -= 3; p < a && p < end; p++) { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + for (end -= 3; p < end; p += 4) { + uint32_t x = *(const uint32_t*)p; +// if ((x - 0x01000100) & (~x) & 0x80008000) // little endian +// if ((x - 0x00010001) & (~x) & 0x00800080) // big endian + if ((x - 0x01010101) & (~x) & 0x80808080) { // generic + if (p[1] == 0) { + if (p[0] == 0 && p[2] == 1) + return p; + if (p[2] == 0 && p[3] == 1) + return p+1; + } + if (p[3] == 0) { + if (p[2] == 0 && p[4] == 1) + return p+2; + if (p[4] == 0 && p[5] == 1) + return p+3; + } + } + } + + for (end += 3; p < end; p++) { + if (p[0] == 0 && p[1] == 0 && p[2] == 1) + return p; + } + + return end + 3; +} + +const uint8_t *ff_avc_find_startcode(const uint8_t *p, const uint8_t *end){ + const uint8_t *out= ff_avc_find_startcode_internal(p, end); + if(p 6) { + /* check for h264 start code */ + if (AV_RB32(data) == 0x00000001 || + AV_RB24(data) == 0x000001) { + uint8_t *buf=NULL, *end, *start; + uint32_t sps_size=0, pps_size=0; + uint8_t *sps=0, *pps=0; + + int ret = ff_avc_parse_nal_units_buf(data, &buf, &len); + if (ret < 0) + return ret; + start = buf; + end = buf + len; + + /* look for sps and pps */ + while (end - buf > 4) { + uint32_t size; + uint8_t nal_type; + size = FFMIN(AV_RB32(buf), end - buf - 4); + buf += 4; + nal_type = buf[0] & 0x1f; + + if (nal_type == 7) { /* SPS */ + sps = buf; + sps_size = size; + } else if (nal_type == 8) { /* PPS */ + pps = buf; + pps_size = size; + } + + buf += size; + } + + if (!sps || !pps || sps_size < 4 || sps_size > UINT16_MAX || pps_size > UINT16_MAX) + return AVERROR_INVALIDDATA; + + avio_w8(pb, 1); /* version */ + avio_w8(pb, sps[1]); /* profile */ + avio_w8(pb, sps[2]); /* profile compat */ + avio_w8(pb, sps[3]); /* level */ + avio_w8(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ + avio_w8(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ + + avio_wb16(pb, sps_size); + avio_write(pb, sps, sps_size); + avio_w8(pb, 1); /* number of pps */ + avio_wb16(pb, pps_size); + avio_write(pb, pps, pps_size); + av_free(start); + } else { + avio_write(pb, data, len); + } + } + return 0; +} + +int ff_avc_write_annexb_extradata(const uint8_t *in, uint8_t **buf, int *size) +{ + uint16_t sps_size, pps_size; + uint8_t *out; + int out_size; + + *buf = NULL; + if (*size >= 4 && (AV_RB32(in) == 0x00000001 || AV_RB24(in) == 0x000001)) + return 0; + if (*size < 11 || in[0] != 1) + return AVERROR_INVALIDDATA; + + sps_size = AV_RB16(&in[6]); + if (11 + sps_size > *size) + return AVERROR_INVALIDDATA; + pps_size = AV_RB16(&in[9 + sps_size]); + if (11 + sps_size + pps_size > *size) + return AVERROR_INVALIDDATA; + out_size = 8 + sps_size + pps_size; + out = av_mallocz(out_size); + if (!out) + return AVERROR(ENOMEM); + AV_WB32(&out[0], 0x00000001); + memcpy(out + 4, &in[8], sps_size); + AV_WB32(&out[4 + sps_size], 0x00000001); + memcpy(out + 8 + sps_size, &in[11 + sps_size], pps_size); + *buf = out; + *size = out_size; + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/avc.o libavformat/avc.o: libavformat/avc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avc.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,34 @@ +/* + * AVC helper functions for muxers + * Copyright (c) 2008 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVC_H +#define AVFORMAT_AVC_H + +#include +#include "avio.h" + +int ff_avc_parse_nal_units(AVIOContext *s, const uint8_t *buf, int size); +int ff_avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size); +int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len); +const uint8_t *ff_avc_find_startcode(const uint8_t *p, const uint8_t *end); +int ff_avc_write_annexb_extradata(const uint8_t *in, uint8_t **buf, int *size); + +#endif /* AVFORMAT_AVC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avc.o Binary file ffmpeg/libavformat/avc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avformat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avformat.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,2151 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVFORMAT_H +#define AVFORMAT_AVFORMAT_H + +/** + * @file + * @ingroup libavf + * Main libavformat public API header + */ + +/** + * @defgroup libavf I/O and Muxing/Demuxing Library + * @{ + * + * Libavformat (lavf) is a library for dealing with various media container + * formats. Its main two purposes are demuxing - i.e. splitting a media file + * into component streams, and the reverse process of muxing - writing supplied + * data in a specified container format. It also has an @ref lavf_io + * "I/O module" which supports a number of protocols for accessing the data (e.g. + * file, tcp, http and others). Before using lavf, you need to call + * av_register_all() to register all compiled muxers, demuxers and protocols. + * Unless you are absolutely sure you won't use libavformat's network + * capabilities, you should also call avformat_network_init(). + * + * A supported input format is described by an AVInputFormat struct, conversely + * an output format is described by AVOutputFormat. You can iterate over all + * registered input/output formats using the av_iformat_next() / + * av_oformat_next() functions. The protocols layer is not part of the public + * API, so you can only get the names of supported protocols with the + * avio_enum_protocols() function. + * + * Main lavf structure used for both muxing and demuxing is AVFormatContext, + * which exports all information about the file being read or written. As with + * most Libavformat structures, its size is not part of public ABI, so it cannot be + * allocated on stack or directly with av_malloc(). To create an + * AVFormatContext, use avformat_alloc_context() (some functions, like + * avformat_open_input() might do that for you). + * + * Most importantly an AVFormatContext contains: + * @li the @ref AVFormatContext.iformat "input" or @ref AVFormatContext.oformat + * "output" format. It is either autodetected or set by user for input; + * always set by user for output. + * @li an @ref AVFormatContext.streams "array" of AVStreams, which describe all + * elementary streams stored in the file. AVStreams are typically referred to + * using their index in this array. + * @li an @ref AVFormatContext.pb "I/O context". It is either opened by lavf or + * set by user for input, always set by user for output (unless you are dealing + * with an AVFMT_NOFILE format). + * + * @section lavf_options Passing options to (de)muxers + * Lavf allows to configure muxers and demuxers using the @ref avoptions + * mechanism. Generic (format-independent) libavformat options are provided by + * AVFormatContext, they can be examined from a user program by calling + * av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass + * from avformat_get_class()). Private (format-specific) options are provided by + * AVFormatContext.priv_data if and only if AVInputFormat.priv_class / + * AVOutputFormat.priv_class of the corresponding format struct is non-NULL. + * Further options may be provided by the @ref AVFormatContext.pb "I/O context", + * if its AVClass is non-NULL, and the protocols layer. See the discussion on + * nesting in @ref avoptions documentation to learn how to access those. + * + * @defgroup lavf_decoding Demuxing + * @{ + * Demuxers read a media file and split it into chunks of data (@em packets). A + * @ref AVPacket "packet" contains one or more encoded frames which belongs to a + * single elementary stream. In the lavf API this process is represented by the + * avformat_open_input() function for opening a file, av_read_frame() for + * reading a single packet and finally avformat_close_input(), which does the + * cleanup. + * + * @section lavf_decoding_open Opening a media file + * The minimum information required to open a file is its URL or filename, which + * is passed to avformat_open_input(), as in the following code: + * @code + * const char *url = "in.mp3"; + * AVFormatContext *s = NULL; + * int ret = avformat_open_input(&s, url, NULL, NULL); + * if (ret < 0) + * abort(); + * @endcode + * The above code attempts to allocate an AVFormatContext, open the + * specified file (autodetecting the format) and read the header, exporting the + * information stored there into s. Some formats do not have a header or do not + * store enough information there, so it is recommended that you call the + * avformat_find_stream_info() function which tries to read and decode a few + * frames to find missing information. + * + * In some cases you might want to preallocate an AVFormatContext yourself with + * avformat_alloc_context() and do some tweaking on it before passing it to + * avformat_open_input(). One such case is when you want to use custom functions + * for reading input data instead of lavf internal I/O layer. + * To do that, create your own AVIOContext with avio_alloc_context(), passing + * your reading callbacks to it. Then set the @em pb field of your + * AVFormatContext to newly created AVIOContext. + * + * Since the format of the opened file is in general not known until after + * avformat_open_input() has returned, it is not possible to set demuxer private + * options on a preallocated context. Instead, the options should be passed to + * avformat_open_input() wrapped in an AVDictionary: + * @code + * AVDictionary *options = NULL; + * av_dict_set(&options, "video_size", "640x480", 0); + * av_dict_set(&options, "pixel_format", "rgb24", 0); + * + * if (avformat_open_input(&s, url, NULL, &options) < 0) + * abort(); + * av_dict_free(&options); + * @endcode + * This code passes the private options 'video_size' and 'pixel_format' to the + * demuxer. They would be necessary for e.g. the rawvideo demuxer, since it + * cannot know how to interpret raw video data otherwise. If the format turns + * out to be something different than raw video, those options will not be + * recognized by the demuxer and therefore will not be applied. Such unrecognized + * options are then returned in the options dictionary (recognized options are + * consumed). The calling program can handle such unrecognized options as it + * wishes, e.g. + * @code + * AVDictionaryEntry *e; + * if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + * fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key); + * abort(); + * } + * @endcode + * + * After you have finished reading the file, you must close it with + * avformat_close_input(). It will free everything associated with the file. + * + * @section lavf_decoding_read Reading from an opened file + * Reading data from an opened AVFormatContext is done by repeatedly calling + * av_read_frame() on it. Each call, if successful, will return an AVPacket + * containing encoded data for one AVStream, identified by + * AVPacket.stream_index. This packet may be passed straight into the libavcodec + * decoding functions avcodec_decode_video2(), avcodec_decode_audio4() or + * avcodec_decode_subtitle2() if the caller wishes to decode the data. + * + * AVPacket.pts, AVPacket.dts and AVPacket.duration timing information will be + * set if known. They may also be unset (i.e. AV_NOPTS_VALUE for + * pts/dts, 0 for duration) if the stream does not provide them. The timing + * information will be in AVStream.time_base units, i.e. it has to be + * multiplied by the timebase to convert them to seconds. + * + * If AVPacket.buf is set on the returned packet, then the packet is + * allocated dynamically and the user may keep it indefinitely. + * Otherwise, if AVPacket.buf is NULL, the packet data is backed by a + * static storage somewhere inside the demuxer and the packet is only valid + * until the next av_read_frame() call or closing the file. If the caller + * requires a longer lifetime, av_dup_packet() will make an av_malloc()ed copy + * of it. + * In both cases, the packet must be freed with av_free_packet() when it is no + * longer needed. + * + * @section lavf_decoding_seek Seeking + * @} + * + * @defgroup lavf_encoding Muxing + * @{ + * @} + * + * @defgroup lavf_io I/O Read/Write + * @{ + * @} + * + * @defgroup lavf_codec Demuxers + * @{ + * @defgroup lavf_codec_native Native Demuxers + * @{ + * @} + * @defgroup lavf_codec_wrappers External library wrappers + * @{ + * @} + * @} + * @defgroup lavf_protos I/O Protocols + * @{ + * @} + * @defgroup lavf_internal Internal + * @{ + * @} + * @} + * + */ + +#include +#include /* FILE */ +#include "libavcodec/avcodec.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "avio.h" +#include "libavformat/version.h" + +struct AVFormatContext; + + +/** + * @defgroup metadata_api Public Metadata API + * @{ + * @ingroup libavf + * The metadata API allows libavformat to export metadata tags to a client + * application when demuxing. Conversely it allows a client application to + * set metadata when muxing. + * + * Metadata is exported or set as pairs of key/value strings in the 'metadata' + * fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs + * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg, + * metadata is assumed to be UTF-8 encoded Unicode. Note that metadata + * exported by demuxers isn't checked to be valid UTF-8 in most cases. + * + * Important concepts to keep in mind: + * - Keys are unique; there can never be 2 tags with the same key. This is + * also meant semantically, i.e., a demuxer should not knowingly produce + * several keys that are literally different but semantically identical. + * E.g., key=Author5, key=Author6. In this example, all authors must be + * placed in the same tag. + * - Metadata is flat, not hierarchical; there are no subtags. If you + * want to store, e.g., the email address of the child of producer Alice + * and actor Bob, that could have key=alice_and_bobs_childs_email_address. + * - Several modifiers can be applied to the tag name. This is done by + * appending a dash character ('-') and the modifier name in the order + * they appear in the list below -- e.g. foo-eng-sort, not foo-sort-eng. + * - language -- a tag whose value is localized for a particular language + * is appended with the ISO 639-2/B 3-letter language code. + * For example: Author-ger=Michael, Author-eng=Mike + * The original/default language is in the unqualified "Author" tag. + * A demuxer should set a default if it sets any translated tag. + * - sorting -- a modified version of a tag that should be used for + * sorting will have '-sort' appended. E.g. artist="The Beatles", + * artist-sort="Beatles, The". + * + * - Demuxers attempt to export metadata in a generic format, however tags + * with no generic equivalents are left as they are stored in the container. + * Follows a list of generic tag names: + * + @verbatim + album -- name of the set this work belongs to + album_artist -- main creator of the set/album, if different from artist. + e.g. "Various Artists" for compilation albums. + artist -- main creator of the work + comment -- any additional description of the file. + composer -- who composed the work, if different from artist. + copyright -- name of copyright holder. + creation_time-- date when the file was created, preferably in ISO 8601. + date -- date when the work was created, preferably in ISO 8601. + disc -- number of a subset, e.g. disc in a multi-disc collection. + encoder -- name/settings of the software/hardware that produced the file. + encoded_by -- person/group who created the file. + filename -- original name of the file. + genre -- . + language -- main language in which the work is performed, preferably + in ISO 639-2 format. Multiple languages can be specified by + separating them with commas. + performer -- artist who performed the work, if different from artist. + E.g for "Also sprach Zarathustra", artist would be "Richard + Strauss" and performer "London Philharmonic Orchestra". + publisher -- name of the label/publisher. + service_name -- name of the service in broadcasting (channel name). + service_provider -- name of the service provider in broadcasting. + title -- name of the work. + track -- number of this work in the set, can be in form current/total. + variant_bitrate -- the total bitrate of the bitrate variant that the current stream is part of + @endverbatim + * + * Look in the examples section for an application example how to use the Metadata API. + * + * @} + */ + +/* packet functions */ + + +/** + * Allocate and read the payload of a packet and initialize its + * fields with default values. + * + * @param pkt packet + * @param size desired payload size + * @return >0 (read size) if OK, AVERROR_xxx otherwise + */ +int av_get_packet(AVIOContext *s, AVPacket *pkt, int size); + + +/** + * Read data and append it to the current content of the AVPacket. + * If pkt->size is 0 this is identical to av_get_packet. + * Note that this uses av_grow_packet and thus involves a realloc + * which is inefficient. Thus this function should only be used + * when there is no reasonable way to know (an upper bound of) + * the final size. + * + * @param pkt packet + * @param size amount of data to read + * @return >0 (read size) if OK, AVERROR_xxx otherwise, previous data + * will not be lost even if an error occurs. + */ +int av_append_packet(AVIOContext *s, AVPacket *pkt, int size); + +/*************************************************/ +/* fractional numbers for exact pts handling */ + +/** + * The exact value of the fractional number is: 'val + num / den'. + * num is assumed to be 0 <= num < den. + */ +typedef struct AVFrac { + int64_t val, num, den; +} AVFrac; + +/*************************************************/ +/* input/output formats */ + +struct AVCodecTag; + +/** + * This structure contains the data a format has to probe a file. + */ +typedef struct AVProbeData { + const char *filename; + unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */ + int buf_size; /**< Size of buf except extra allocated bytes */ +} AVProbeData; + +#define AVPROBE_SCORE_MAX 100 ///< maximum score, half of that is used for file-extension-based detection +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) +#define AVPROBE_PADDING_SIZE 32 ///< extra allocated bytes at the end of the probe buffer + +/// Demuxer will use avio_open, no opened file should be provided by the caller. +#define AVFMT_NOFILE 0x0001 +#define AVFMT_NEEDNUMBER 0x0002 /**< Needs '%d' in filename. */ +#define AVFMT_SHOW_IDS 0x0008 /**< Show format stream IDs numbers. */ +#define AVFMT_RAWPICTURE 0x0020 /**< Format wants AVPicture structure for + raw picture data. */ +#define AVFMT_GLOBALHEADER 0x0040 /**< Format wants global header. */ +#define AVFMT_NOTIMESTAMPS 0x0080 /**< Format does not need / have any timestamps. */ +#define AVFMT_GENERIC_INDEX 0x0100 /**< Use generic index building code. */ +#define AVFMT_TS_DISCONT 0x0200 /**< Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps */ +#define AVFMT_VARIABLE_FPS 0x0400 /**< Format allows variable fps. */ +#define AVFMT_NODIMENSIONS 0x0800 /**< Format does not need width/height */ +#define AVFMT_NOSTREAMS 0x1000 /**< Format does not require any streams */ +#define AVFMT_NOBINSEARCH 0x2000 /**< Format does not allow to fallback to binary search via read_timestamp */ +#define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fallback to generic search */ +#define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */ +#define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */ +#if LIBAVFORMAT_VERSION_MAJOR <= 54 +#define AVFMT_TS_NONSTRICT 0x8020000 //we try to be compatible to the ABIs of ffmpeg and major forks +#else +#define AVFMT_TS_NONSTRICT 0x20000 +#endif + /**< Format does not require strictly + increasing timestamps, but they must + still be monotonic */ + +#define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ + +/** + * @addtogroup lavf_encoding + * @{ + */ +typedef struct AVOutputFormat { + const char *name; + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + const char *mime_type; + const char *extensions; /**< comma-separated filename extensions */ + /* output support */ + enum AVCodecID audio_codec; /**< default audio codec */ + enum AVCodecID video_codec; /**< default video codec */ + enum AVCodecID subtitle_codec; /**< default subtitle codec */ + /** + * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_RAWPICTURE, + * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS, + * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH, + * AVFMT_TS_NONSTRICT + */ + int flags; + + /** + * List of supported codec_id-codec_tag pairs, ordered by "better + * choice first". The arrays are all terminated by AV_CODEC_ID_NONE. + */ + const struct AVCodecTag * const *codec_tag; + + + const AVClass *priv_class; ///< AVClass for the private context + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + struct AVOutputFormat *next; + /** + * size of private data so that it can be allocated in the wrapper + */ + int priv_data_size; + + int (*write_header)(struct AVFormatContext *); + /** + * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, + * pkt can be NULL in order to flush data buffered in the muxer. + * When flushing, return 0 if there still is more data to flush, + * or 1 if everything was flushed and there is no more buffered + * data. + */ + int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); + int (*write_trailer)(struct AVFormatContext *); + /** + * Currently only used to set pixel format if not YUV420P. + */ + int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, + AVPacket *in, int flush); + /** + * Test if the given codec can be stored in this container. + * + * @return 1 if the codec is supported, 0 if it is not. + * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC + */ + int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); +} AVOutputFormat; +/** + * @} + */ + +/** + * @addtogroup lavf_decoding + * @{ + */ +typedef struct AVInputFormat { + /** + * A comma separated list of short names for the format. New names + * may be appended with a minor bump. + */ + const char *name; + + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + + /** + * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, + * AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, + * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. + */ + int flags; + + /** + * If extensions are defined, then no probe is done. You should + * usually not use extension format guessing because it is not + * reliable enough + */ + const char *extensions; + + const struct AVCodecTag * const *codec_tag; + + const AVClass *priv_class; ///< AVClass for the private context + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + struct AVInputFormat *next; + + /** + * Raw demuxers store their codec ID here. + */ + int raw_codec_id; + + /** + * Size of private data so that it can be allocated in the wrapper. + */ + int priv_data_size; + + /** + * Tell if a given file has a chance of being parsed as this format. + * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes + * big so you do not have to check for that unless you need more. + */ + int (*read_probe)(AVProbeData *); + + /** + * Read the format header and initialize the AVFormatContext + * structure. Return 0 if OK. Only used in raw format right + * now. 'avformat_new_stream' should be called to create new streams. + */ + int (*read_header)(struct AVFormatContext *); + + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag + * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a + * background thread). + * @return 0 on success, < 0 on error. + * When returning an error, pkt must not have been allocated + * or must be freed before returning + */ + int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); + + /** + * Close the stream. The AVFormatContext and AVStreams are not + * freed by this function + */ + int (*read_close)(struct AVFormatContext *); + + /** + * Seek to a given timestamp relative to the frames in + * stream component stream_index. + * @param stream_index Must not be -1. + * @param flags Selects which direction should be preferred if no exact + * match is available. + * @return >= 0 on success (but not necessarily the new offset) + */ + int (*read_seek)(struct AVFormatContext *, + int stream_index, int64_t timestamp, int flags); + + /** + * Get the next timestamp in stream[stream_index].time_base units. + * @return the timestamp or AV_NOPTS_VALUE if an error occurred + */ + int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit); + + /** + * Start/resume playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_play)(struct AVFormatContext *); + + /** + * Pause playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_pause)(struct AVFormatContext *); + + /** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + */ + int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); +} AVInputFormat; +/** + * @} + */ + +enum AVStreamParseType { + AVSTREAM_PARSE_NONE, + AVSTREAM_PARSE_FULL, /**< full parsing and repack */ + AVSTREAM_PARSE_HEADERS, /**< Only parse headers, do not repack. */ + AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */ + AVSTREAM_PARSE_FULL_ONCE, /**< full parsing and repack of the first frame only, only implemented for H.264 currently */ + AVSTREAM_PARSE_FULL_RAW=MKTAG(0,'R','A','W'), /**< full parsing and repack with timestamp and position generation by parser for raw + this assumes that each packet in the file contains no demuxer level headers and + just codec level data, otherwise position generation would fail */ +}; + +typedef struct AVIndexEntry { + int64_t pos; + int64_t timestamp; /**< + * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available + * when seeking to this entry. That means preferable PTS on keyframe based formats. + * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better + * is known + */ +#define AVINDEX_KEYFRAME 0x0001 + int flags:2; + int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment). + int min_distance; /**< Minimum distance between this and the previous keyframe, used to avoid unneeded searching. */ +} AVIndexEntry; + +#define AV_DISPOSITION_DEFAULT 0x0001 +#define AV_DISPOSITION_DUB 0x0002 +#define AV_DISPOSITION_ORIGINAL 0x0004 +#define AV_DISPOSITION_COMMENT 0x0008 +#define AV_DISPOSITION_LYRICS 0x0010 +#define AV_DISPOSITION_KARAOKE 0x0020 + +/** + * Track should be used during playback by default. + * Useful for subtitle track that should be displayed + * even when user did not explicitly ask for subtitles. + */ +#define AV_DISPOSITION_FORCED 0x0040 +#define AV_DISPOSITION_HEARING_IMPAIRED 0x0080 /**< stream for hearing impaired audiences */ +#define AV_DISPOSITION_VISUAL_IMPAIRED 0x0100 /**< stream for visual impaired audiences */ +#define AV_DISPOSITION_CLEAN_EFFECTS 0x0200 /**< stream without voice */ +/** + * The stream is stored in the file as an attached picture/"cover art" (e.g. + * APIC frame in ID3v2). The single packet associated with it will be returned + * among the first few packets read from the file unless seeking takes place. + * It can also be accessed at any time in AVStream.attached_pic. + */ +#define AV_DISPOSITION_ATTACHED_PIC 0x0400 + +/** + * Options for behavior on timestamp wrap detection. + */ +#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap +#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection +#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection + +/** + * Stream structure. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVStream) must not be used outside libav*. + */ +typedef struct AVStream { + int index; /**< stream index in AVFormatContext */ + /** + * Format-specific stream ID. + * decoding: set by libavformat + * encoding: set by the user, replaced by libavformat if left unset + */ + int id; + /** + * Codec context associated with this stream. Allocated and freed by + * libavformat. + * + * - decoding: The demuxer exports codec information stored in the headers + * here. + * - encoding: The user sets codec information, the muxer writes it to the + * output. Mandatory fields as specified in AVCodecContext + * documentation must be set even if this AVCodecContext is + * not actually used for encoding. + */ + AVCodecContext *codec; + void *priv_data; + + /** + * encoding: pts generation when outputting stream + */ + struct AVFrac pts; + + /** + * This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. + * + * decoding: set by libavformat + * encoding: set by libavformat in avformat_write_header. The muxer may use the + * user-provided value of @ref AVCodecContext.time_base "codec->time_base" + * as a hint. + */ + AVRational time_base; + + /** + * Decoding: pts of the first frame of the stream in presentation order, in stream time base. + * Only set this if you are absolutely 100% sure that the value you set + * it to really is the pts of the first frame. + * This may be undefined (AV_NOPTS_VALUE). + * @note The ASF header does NOT contain a correct start_time the ASF + * demuxer must NOT set this. + */ + int64_t start_time; + + /** + * Decoding: duration of the stream, in stream time base. + * If a source file does not specify a duration, but does specify + * a bitrate, this value will be estimated from bitrate and file size. + */ + int64_t duration; + + int64_t nb_frames; ///< number of frames in this stream if known or 0 + + int disposition; /**< AV_DISPOSITION_* bit field */ + + enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed. + + /** + * sample aspect ratio (0 if unknown) + * - encoding: Set by user. + * - decoding: Set by libavformat. + */ + AVRational sample_aspect_ratio; + + AVDictionary *metadata; + + /** + * Average framerate + */ + AVRational avg_frame_rate; + + /** + * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet + * will contain the attached picture. + * + * decoding: set by libavformat, must not be modified by the caller. + * encoding: unused + */ + AVPacket attached_pic; + + /** + * Real base framerate of the stream. + * This is the lowest framerate with which all timestamps can be + * represented accurately (it is the least common multiple of all + * framerates in the stream). Note, this value is just a guess! + * For example, if the time base is 1/90000 and all frames have either + * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1. + * + * Code outside avformat should access this field using: + * av_stream_get/set_r_frame_rate(stream) + */ + AVRational r_frame_rate; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + /** + * Stream information used internally by av_find_stream_info() + */ +#define MAX_STD_TIMEBASES (60*12+6) + struct { + int64_t last_dts; + int64_t duration_gcd; + int duration_count; + double (*duration_error)[2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; + int found_decoder; + + int64_t last_duration; + + /** + * Those are used for average framerate estimation. + */ + int64_t fps_first_dts; + int fps_first_dts_idx; + int64_t fps_last_dts; + int fps_last_dts_idx; + + } *info; + + int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ + + // Timestamp generation support: + /** + * Timestamp corresponding to the last dts sync point. + * + * Initialized when AVCodecParserContext.dts_sync_point >= 0 and + * a DTS is received from the underlying container. Otherwise set to + * AV_NOPTS_VALUE by default. + */ + int64_t reference_dts; + int64_t first_dts; + int64_t cur_dts; + int64_t last_IP_pts; + int last_IP_duration; + + /** + * Number of packets to buffer for codec probing + */ +#define MAX_PROBE_PACKETS 2500 + int probe_packets; + + /** + * Number of frames that have been demuxed during av_find_stream_info() + */ + int codec_info_nb_frames; + + /** + * Stream Identifier + * This is the MPEG-TS stream identifier +1 + * 0 means unknown + */ + int stream_identifier; + + int64_t interleaver_chunk_size; + int64_t interleaver_chunk_duration; + + /* av_read_frame() support */ + enum AVStreamParseType need_parsing; + struct AVCodecParserContext *parser; + + /** + * last packet in packet_buffer for this stream when muxing. + */ + struct AVPacketList *last_in_packet_buffer; + AVProbeData probe_data; +#define MAX_REORDER_DELAY 16 + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + + AVIndexEntry *index_entries; /**< Only used if the format does not + support seeking natively. */ + int nb_index_entries; + unsigned int index_entries_allocated_size; + + /** + * stream probing state + * -1 -> probing finished + * 0 -> no probing requested + * rest -> perform probing with request_probe being the minimum score to accept. + * NOT PART OF PUBLIC API + */ + int request_probe; + /** + * Indicates that everything up to the next keyframe + * should be discarded. + */ + int skip_to_keyframe; + + /** + * Number of samples to skip at the start of the frame decoded from the next packet. + */ + int skip_samples; + + /** + * Number of internally decoded frames, used internally in libavformat, do not access + * its lifetime differs from info which is why it is not in that structure. + */ + int nb_decoded_frames; + + /** + * Timestamp offset added to timestamps before muxing + * NOT PART OF PUBLIC API + */ + int64_t mux_ts_offset; + + /** + * Internal data to check for wrapping of the time stamp + */ + int64_t pts_wrap_reference; + + /** + * Options for behavior, when a wrap is detected. + * + * Defined by AV_PTS_WRAP_ values. + * + * If correction is enabled, there are two possibilities: + * If the first time stamp is near the wrap point, the wrap offset + * will be subtracted, which will create negative time stamps. + * Otherwise the offset will be added. + */ + int pts_wrap_behavior; + +} AVStream; + +AVRational av_stream_get_r_frame_rate(const AVStream *s); +void av_stream_set_r_frame_rate(AVStream *s, AVRational r); + +#define AV_PROGRAM_RUNNING 1 + +/** + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVProgram) must not be used outside libav*. + */ +typedef struct AVProgram { + int id; + int flags; + enum AVDiscard discard; ///< selects which program to discard and which to feed to the caller + unsigned int *stream_index; + unsigned int nb_stream_indexes; + AVDictionary *metadata; + + int program_num; + int pmt_pid; + int pcr_pid; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int64_t start_time; + int64_t end_time; + + int64_t pts_wrap_reference; ///< reference dts for wrap detection + int pts_wrap_behavior; ///< behavior on wrap detection +} AVProgram; + +#define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present + (streams are added dynamically) */ + +typedef struct AVChapter { + int id; ///< unique ID to identify the chapter + AVRational time_base; ///< time base in which the start/end timestamps are specified + int64_t start, end; ///< chapter start/end time in time_base units + AVDictionary *metadata; +} AVChapter; + + +/** + * The duration of a video can be estimated through various ways, and this enum can be used + * to know how the duration was estimated. + */ +enum AVDurationEstimationMethod { + AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes + AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration + AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) +}; + +/** + * Format I/O context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVFormatContext) must not be used outside libav*, use + * avformat_alloc_context() to create an AVFormatContext. + */ +typedef struct AVFormatContext { + /** + * A class for logging and AVOptions. Set by avformat_alloc_context(). + * Exports (de)muxer private options if they exist. + */ + const AVClass *av_class; + + /** + * Can only be iformat or oformat, not both at the same time. + * + * decoding: set by avformat_open_input(). + * encoding: set by the user. + */ + struct AVInputFormat *iformat; + struct AVOutputFormat *oformat; + + /** + * Format private data. This is an AVOptions-enabled struct + * if and only if iformat/oformat.priv_class is not NULL. + */ + void *priv_data; + + /** + * I/O context. + * + * decoding: either set by the user before avformat_open_input() (then + * the user must close it manually) or set by avformat_open_input(). + * encoding: set by the user. + * + * Do NOT set this field if AVFMT_NOFILE flag is set in + * iformat/oformat.flags. In such a case, the (de)muxer will handle + * I/O in some other way and this field will be NULL. + */ + AVIOContext *pb; + + /* stream info */ + int ctx_flags; /**< Format-specific flags, see AVFMTCTX_xx */ + + /** + * A list of all streams in the file. New streams are created with + * avformat_new_stream(). + * + * decoding: streams are created by libavformat in avformat_open_input(). + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also + * appear in av_read_frame(). + * encoding: streams are created by the user before avformat_write_header(). + */ + unsigned int nb_streams; + AVStream **streams; + + char filename[1024]; /**< input or output filename */ + + /** + * Decoding: position of the first frame of the component, in + * AV_TIME_BASE fractional seconds. NEVER set this value directly: + * It is deduced from the AVStream values. + */ + int64_t start_time; + + /** + * Decoding: duration of the stream, in AV_TIME_BASE fractional + * seconds. Only set this value if you know none of the individual stream + * durations and also do not set any of them. This is deduced from the + * AVStream values if not set. + */ + int64_t duration; + + /** + * Decoding: total stream bitrate in bit/s, 0 if not + * available. Never set it directly if the file_size and the + * duration are known as FFmpeg can compute it automatically. + */ + int bit_rate; + + unsigned int packet_size; + int max_delay; + + int flags; +#define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. +#define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. +#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS +#define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container +#define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled +#define AVFMT_FLAG_NOBUFFER 0x0040 ///< Do not buffer frames when possible +#define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it. +#define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload +#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) +#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) +#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. + + /** + * decoding: size of data to probe; encoding: unused. + */ + unsigned int probesize; + + /** + * decoding: maximum time (in AV_TIME_BASE units) during which the input should + * be analyzed in avformat_find_stream_info(). + */ + int max_analyze_duration; + + const uint8_t *key; + int keylen; + + unsigned int nb_programs; + AVProgram **programs; + + /** + * Forced video codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID video_codec_id; + + /** + * Forced audio codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID audio_codec_id; + + /** + * Forced subtitle codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID subtitle_codec_id; + + /** + * Maximum amount of memory in bytes to use for the index of each stream. + * If the index exceeds this size, entries will be discarded as + * needed to maintain a smaller size. This can lead to slower or less + * accurate seeking (depends on demuxer). + * Demuxers for which a full in-memory index is mandatory will ignore + * this. + * muxing : unused + * demuxing: set by user + */ + unsigned int max_index_size; + + /** + * Maximum amount of memory in bytes to use for buffering frames + * obtained from realtime capture devices. + */ + unsigned int max_picture_buffer; + + unsigned int nb_chapters; + AVChapter **chapters; + + AVDictionary *metadata; + + /** + * Start time of the stream in real world time, in microseconds + * since the unix epoch (00:00 1st January 1970). That is, pts=0 + * in the stream was captured at this real world time. + * - encoding: Set by user. + * - decoding: Unused. + */ + int64_t start_time_realtime; + + /** + * decoding: number of frames used to probe fps + */ + int fps_probe_size; + + /** + * Error recognition; higher values will detect more errors but may + * misdetect some more or less valid parts as errors. + * - encoding: unused + * - decoding: Set by user. + */ + int error_recognition; + + /** + * Custom interrupt callbacks for the I/O layer. + * + * decoding: set by the user before avformat_open_input(). + * encoding: set by the user before avformat_write_header() + * (mainly useful for AVFMT_NOFILE formats). The callback + * should also be passed to avio_open2() if it's used to + * open the file. + */ + AVIOInterruptCB interrupt_callback; + + /** + * Flags to enable debugging. + */ + int debug; +#define FF_FDEBUG_TS 0x0001 + + /** + * Transport stream id. + * This will be moved into demuxer private options. Thus no API/ABI compatibility + */ + int ts_id; + + /** + * Audio preload in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int audio_preload; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int max_chunk_size; + + /** + * forces the use of wallclock timestamps as pts/dts of packets + * This has undefined results in the presence of B frames. + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + int use_wallclock_as_timestamps; + + /** + * Avoid negative timestamps during muxing. + * 0 -> allow negative timestamps + * 1 -> avoid negative timestamps + * -1 -> choose automatically (default) + * Note, this only works when interleave_packet_per_dts is in use. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int avoid_negative_ts; + + /** + * avio flags, used to force AVIO_FLAG_DIRECT. + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + int avio_flags; + + /** + * The duration field can be estimated through various ways, and this field can be used + * to know how the duration was estimated. + * - encoding: unused + * - decoding: Read by user via AVOptions (NO direct access) + */ + enum AVDurationEstimationMethod duration_estimation_method; + + /** + * Skip initial bytes when opening stream + * - encoding: unused + * - decoding: Set by user via AVOptions (NO direct access) + */ + unsigned int skip_initial_bytes; + + /** + * Correct single timestamp overflows + * - encoding: unused + * - decoding: Set by user via AVOPtions (NO direct access) + */ + unsigned int correct_ts_overflow; + + /** + * Force seeking to any (also non key) frames. + * - encoding: unused + * - decoding: Set by user via AVOPtions (NO direct access) + */ + int seek2any; + + /** + * Flush the I/O context after each packet. + * - encoding: Set by user via AVOptions (NO direct access) + * - decoding: unused + */ + int flush_packets; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + /** + * This buffer is only needed when packets were already buffered but + * not decoded, for example to get the codec parameters in MPEG + * streams. + */ + struct AVPacketList *packet_buffer; + struct AVPacketList *packet_buffer_end; + + /* av_seek_frame() support */ + int64_t data_offset; /**< offset of the first packet */ + + /** + * Raw packets from the demuxer, prior to parsing and decoding. + * This buffer is used for buffering packets until the codec can + * be identified, as parsing cannot be done without knowing the + * codec. + */ + struct AVPacketList *raw_packet_buffer; + struct AVPacketList *raw_packet_buffer_end; + /** + * Packets split by the parser get queued here. + */ + struct AVPacketList *parse_queue; + struct AVPacketList *parse_queue_end; + /** + * Remaining size available for raw_packet_buffer, in bytes. + */ +#define RAW_PACKET_BUFFER_SIZE 2500000 + int raw_packet_buffer_remaining_size; + + /** + * IO repositioned flag. + * This is set by avformat when the underlaying IO context read pointer + * is repositioned, for example when doing byte based seeking. + * Demuxers can use the flag to detect such changes. + */ + int io_repositioned; +} AVFormatContext; + +/** + * Returns the method used to set ctx->duration. + * + * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE. + */ +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx); + +typedef struct AVPacketList { + AVPacket pkt; + struct AVPacketList *next; +} AVPacketList; + + +/** + * @defgroup lavf_core Core functions + * @ingroup libavf + * + * Functions for querying libavformat capabilities, allocating core structures, + * etc. + * @{ + */ + +/** + * Return the LIBAVFORMAT_VERSION_INT constant. + */ +unsigned avformat_version(void); + +/** + * Return the libavformat build-time configuration. + */ +const char *avformat_configuration(void); + +/** + * Return the libavformat license. + */ +const char *avformat_license(void); + +/** + * Initialize libavformat and register all the muxers, demuxers and + * protocols. If you do not call this function, then you can select + * exactly which formats you want to support. + * + * @see av_register_input_format() + * @see av_register_output_format() + */ +void av_register_all(void); + +void av_register_input_format(AVInputFormat *format); +void av_register_output_format(AVOutputFormat *format); + +/** + * Do global initialization of network components. This is optional, + * but recommended, since it avoids the overhead of implicitly + * doing the setup for each session. + * + * Calling this function will become mandatory if using network + * protocols at some major version bump. + */ +int avformat_network_init(void); + +/** + * Undo the initialization done by avformat_network_init. + */ +int avformat_network_deinit(void); + +/** + * If f is NULL, returns the first registered input format, + * if f is non-NULL, returns the next registered input format after f + * or NULL if f is the last one. + */ +AVInputFormat *av_iformat_next(AVInputFormat *f); + +/** + * If f is NULL, returns the first registered output format, + * if f is non-NULL, returns the next registered output format after f + * or NULL if f is the last one. + */ +AVOutputFormat *av_oformat_next(AVOutputFormat *f); + +/** + * Allocate an AVFormatContext. + * avformat_free_context() can be used to free the context and everything + * allocated by the framework within it. + */ +AVFormatContext *avformat_alloc_context(void); + +/** + * Free an AVFormatContext and all its streams. + * @param s context to free + */ +void avformat_free_context(AVFormatContext *s); + +/** + * Get the AVClass for AVFormatContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avformat_get_class(void); + +/** + * Add a new stream to a media file. + * + * When demuxing, it is called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, should be called by the user before avformat_write_header(). + * + * @param c If non-NULL, the AVCodecContext corresponding to the new stream + * will be initialized to use this codec. This is needed for e.g. codec-specific + * defaults to be set, so codec should be provided if it is known. + * + * @return newly created stream or NULL on error. + */ +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); + +AVProgram *av_new_program(AVFormatContext *s, int id); + +/** + * @} + */ + + +#if FF_API_PKT_DUMP +attribute_deprecated void av_pkt_dump(FILE *f, AVPacket *pkt, int dump_payload); +attribute_deprecated void av_pkt_dump_log(void *avcl, int level, AVPacket *pkt, + int dump_payload); +#endif + +#if FF_API_ALLOC_OUTPUT_CONTEXT +/** + * @deprecated deprecated in favor of avformat_alloc_output_context2() + */ +attribute_deprecated +AVFormatContext *avformat_alloc_output_context(const char *format, + AVOutputFormat *oformat, + const char *filename); +#endif + +/** + * Allocate an AVFormatContext for an output format. + * avformat_free_context() can be used to free the context and + * everything allocated by the framework within it. + * + * @param *ctx is set to the created format context, or to NULL in + * case of failure + * @param oformat format to use for allocating the context, if NULL + * format_name and filename are used instead + * @param format_name the name of output format to use for allocating the + * context, if NULL filename is used instead + * @param filename the name of the filename to use for allocating the + * context, may be NULL + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure + */ +int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, + const char *format_name, const char *filename); + +/** + * @addtogroup lavf_decoding + * @{ + */ + +/** + * Find AVInputFormat based on the short name of the input format. + */ +AVInputFormat *av_find_input_format(const char *short_name); + +/** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + */ +AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened); + +/** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_max A probe score larger that this is required to accept a + * detection, the variable is set to the actual detection + * score afterwards. + * If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended + * to retry with a larger probe buffer. + */ +AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max); + +/** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_ret The score of the best detection. + */ +AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret); + +/** + * Probe a bytestream to determine the input format. Each time a probe returns + * with a score that is too low, the probe buffer size is increased and another + * attempt is made. When the maximum probe size is reached, the input format + * with the highest score is returned. + * + * @param pb the bytestream to probe + * @param fmt the input format is put here + * @param filename the filename of the stream + * @param logctx the log context + * @param offset the offset within the bytestream to probe from + * @param max_probe_size the maximum probe buffer size (zero for default) + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, + const char *filename, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Open an input stream and read the header. The codecs are not opened. + * The stream must be closed with av_close_input_file(). + * + * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). + * May be a pointer to NULL, in which case an AVFormatContext is allocated by this + * function and written into ps. + * Note that a user-supplied AVFormatContext will be freed on failure. + * @param filename Name of the stream to open. + * @param fmt If non-NULL, this parameter forces a specific input format. + * Otherwise the format is autodetected. + * @param options A dictionary filled with AVFormatContext and demuxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return 0 on success, a negative AVERROR on failure. + * + * @note If you want to use custom IO, preallocate the format context and set its pb field. + */ +int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options); + +attribute_deprecated +int av_demuxer_open(AVFormatContext *ic); + +#if FF_API_FORMAT_PARAMETERS +/** + * Read packets of a media file to get stream information. This + * is useful for file formats with no headers such as MPEG. This + * function also computes the real framerate in case of MPEG-2 repeat + * frame mode. + * The logical file position is not changed by this function; + * examined packets may be buffered for later processing. + * + * @param ic media file handle + * @return >=0 if OK, AVERROR_xxx on error + * @todo Let the user decide somehow what information is needed so that + * we do not waste time getting stuff the user does not need. + * + * @deprecated use avformat_find_stream_info. + */ +attribute_deprecated +int av_find_stream_info(AVFormatContext *ic); +#endif + +/** + * Read packets of a media file to get stream information. This + * is useful for file formats with no headers such as MPEG. This + * function also computes the real framerate in case of MPEG-2 repeat + * frame mode. + * The logical file position is not changed by this function; + * examined packets may be buffered for later processing. + * + * @param ic media file handle + * @param options If non-NULL, an ic.nb_streams long array of pointers to + * dictionaries, where i-th member contains options for + * codec corresponding to i-th stream. + * On return each dictionary will be filled with options that were not found. + * @return >=0 if OK, AVERROR_xxx on error + * + * @note this function isn't guaranteed to open all the codecs, so + * options being non-empty at return is a perfectly normal behavior. + * + * @todo Let the user decide somehow what information is needed so that + * we do not waste time getting stuff the user does not need. + */ +int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); + +/** + * Find the programs which belong to a given stream. + * + * @param ic media file handle + * @param last the last found program, the search will start after this + * program, or from the beginning if it is NULL + * @param s stream index + * @return the next program which belongs to s, NULL if no program is found or + * the last program is not among the programs of ic. + */ +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s); + +/** + * Find the "best" stream in the file. + * The best stream is determined according to various heuristics as the most + * likely to be what the user expects. + * If the decoder parameter is non-NULL, av_find_best_stream will find the + * default decoder for the stream's codec; streams for which no decoder can + * be found are ignored. + * + * @param ic media file handle + * @param type stream type: video, audio, subtitles, etc. + * @param wanted_stream_nb user-requested stream number, + * or -1 for automatic selection + * @param related_stream try to find a stream related (eg. in the same + * program) to this one, or -1 if none + * @param decoder_ret if non-NULL, returns the decoder for the + * selected stream + * @param flags flags; none are currently defined + * @return the non-negative stream number in case of success, + * AVERROR_STREAM_NOT_FOUND if no stream with the requested type + * could be found, + * AVERROR_DECODER_NOT_FOUND if streams were found but no decoder + * @note If av_find_best_stream returns successfully and decoder_ret is not + * NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec. + */ +int av_find_best_stream(AVFormatContext *ic, + enum AVMediaType type, + int wanted_stream_nb, + int related_stream, + AVCodec **decoder_ret, + int flags); + +#if FF_API_READ_PACKET +/** + * @deprecated use AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE to read raw + * unprocessed packets + * + * Read a transport packet from a media file. + * + * This function is obsolete and should never be used. + * Use av_read_frame() instead. + * + * @param s media file handle + * @param pkt is filled + * @return 0 if OK, AVERROR_xxx on error + */ +attribute_deprecated +int av_read_packet(AVFormatContext *s, AVPacket *pkt); +#endif + +/** + * Return the next frame of a stream. + * This function returns what is stored in the file, and does not validate + * that what is there are valid frames for the decoder. It will split what is + * stored in the file into frames and return one for each call. It will not + * omit invalid data between valid frames so as to give the decoder the maximum + * information possible for decoding. + * + * If pkt->buf is NULL, then the packet is valid until the next + * av_read_frame() or until av_close_input_file(). Otherwise the packet is valid + * indefinitely. In both cases the packet must be freed with + * av_free_packet when it is no longer needed. For video, the packet contains + * exactly one frame. For audio, it contains an integer number of frames if each + * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames + * have a variable size (e.g. MPEG audio), then it contains one frame. + * + * pkt->pts, pkt->dts and pkt->duration are always set to correct + * values in AVStream.time_base units (and guessed if the format cannot + * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format + * has B-frames, so it is better to rely on pkt->dts if you do not + * decompress the payload. + * + * @return 0 if OK, < 0 on error or end of file + */ +int av_read_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Seek to the keyframe at timestamp. + * 'timestamp' in 'stream_index'. + * @param stream_index If stream_index is (-1), a default + * stream is selected, and timestamp is automatically converted + * from AV_TIME_BASE units to the stream specific time_base. + * @param timestamp Timestamp in AVStream.time_base units + * or, if no stream is specified, in AV_TIME_BASE units. + * @param flags flags which select direction and seeking mode + * @return >= 0 on success + */ +int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, + int flags); + +/** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + * + * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and + * are the file position (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames + * in the stream with stream_index (this may not be supported by all demuxers). + * Otherwise all timestamps are in units of the stream selected by stream_index + * or if stream_index is -1, in AV_TIME_BASE units. + * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as + * keyframes (this may not be supported by all demuxers). + * + * @param stream_index index of the stream which is used as time base reference + * @param min_ts smallest acceptable timestamp + * @param ts target timestamp + * @param max_ts largest acceptable timestamp + * @param flags flags + * @return >=0 on success, error code otherwise + * + * @note This is part of the new seek API which is still under construction. + * Thus do not use this yet. It may change at any time, do not expect + * ABI compatibility yet! + */ +int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + +/** + * Start playing a network-based stream (e.g. RTSP stream) at the + * current position. + */ +int av_read_play(AVFormatContext *s); + +/** + * Pause a network-based stream (e.g. RTSP stream). + * + * Use av_read_play() to resume it. + */ +int av_read_pause(AVFormatContext *s); + +#if FF_API_CLOSE_INPUT_FILE +/** + * @deprecated use avformat_close_input() + * Close a media file (but not its codecs). + * + * @param s media file handle + */ +attribute_deprecated +void av_close_input_file(AVFormatContext *s); +#endif + +/** + * Close an opened input AVFormatContext. Free it and all its contents + * and set *s to NULL. + */ +void avformat_close_input(AVFormatContext **s); +/** + * @} + */ + +#if FF_API_NEW_STREAM +/** + * Add a new stream to a media file. + * + * Can only be called in the read_header() function. If the flag + * AVFMTCTX_NOHEADER is in the format context, then new streams + * can be added in read_packet too. + * + * @param s media file handle + * @param id file-format-dependent stream ID + */ +attribute_deprecated +AVStream *av_new_stream(AVFormatContext *s, int id); +#endif + +#if FF_API_SET_PTS_INFO +/** + * @deprecated this function is not supposed to be called outside of lavf + */ +attribute_deprecated +void av_set_pts_info(AVStream *s, int pts_wrap_bits, + unsigned int pts_num, unsigned int pts_den); +#endif + +#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward +#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes +#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes +#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number + +/** + * @addtogroup lavf_encoding + * @{ + */ +/** + * Allocate the stream private data and write the stream header to + * an output media file. + * + * @param s Media file handle, must be allocated with avformat_alloc_context(). + * Its oformat field must be set to the desired output format; + * Its pb field must be set to an already openened AVIOContext. + * @param options An AVDictionary filled with AVFormatContext and muxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return 0 on success, negative AVERROR on failure. + * + * @see av_opt_find, av_dict_set, avio_open, av_oformat_next. + */ +int avformat_write_header(AVFormatContext *s, AVDictionary **options); + +/** + * Write a packet to an output media file. + * + * The packet shall contain one audio or video frame. + * The packet must be correctly interleaved according to the container + * specification, if not then av_interleaved_write_frame must be used. + * + * @param s media file handle + * @param pkt The packet, which contains the stream_index, buf/buf_size, + * dts/pts, ... + * This can be NULL (at any time, not just at the end), in + * order to immediately flush data buffered within the muxer, + * for muxers that buffer up data internally before writing it + * to the output. + * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush + */ +int av_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write a packet to an output media file ensuring correct interleaving. + * + * The packet must contain one audio or video frame. + * If the packets are already correctly interleaved, the application should + * call av_write_frame() instead as it is slightly faster. It is also important + * to keep in mind that completely non-interleaved input will need huge amounts + * of memory to interleave with this, so it is preferable to interleave at the + * demuxer level. + * + * @param s media file handle + * @param pkt The packet containing the data to be written. pkt->buf must be set + * to a valid AVBufferRef describing the packet data. Libavformat takes + * ownership of this reference and will unref it when it sees fit. The caller + * must not access the data through this reference after this function returns. + * This can be NULL (at any time, not just at the end), to flush the + * interleaving queues. + * Packet's @ref AVPacket.stream_index "stream_index" field must be set to the + * index of the corresponding stream in @ref AVFormatContext.streams + * "s.streams". + * It is very strongly recommended that timing information (@ref AVPacket.pts + * "pts", @ref AVPacket.dts "dts" @ref AVPacket.duration "duration") is set to + * correct values. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write the stream trailer to an output media file and free the + * file private data. + * + * May only be called after a successful call to avformat_write_header. + * + * @param s media file handle + * @return 0 if OK, AVERROR_xxx on error + */ +int av_write_trailer(AVFormatContext *s); + +/** + * Return the output format in the list of registered output formats + * which best matches the provided parameters, or return NULL if + * there is no match. + * + * @param short_name if non-NULL checks if short_name matches with the + * names of the registered formats + * @param filename if non-NULL checks if filename terminates with the + * extensions of the registered formats + * @param mime_type if non-NULL checks if mime_type matches with the + * MIME type of the registered formats + */ +AVOutputFormat *av_guess_format(const char *short_name, + const char *filename, + const char *mime_type); + +/** + * Guess the codec ID based upon muxer and filename. + */ +enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, + const char *filename, const char *mime_type, + enum AVMediaType type); + +/** + * Get timing information for the data currently output. + * The exact meaning of "currently output" depends on the format. + * It is mostly relevant for devices that have an internal buffer and/or + * work in real time. + * @param s media file handle + * @param stream stream in the media file + * @param[out] dts DTS of the last packet output for the stream, in stream + * time_base units + * @param[out] wall absolute time when that packet whas output, + * in microsecond + * @return 0 if OK, AVERROR(ENOSYS) if the format does not support it + * Note: some formats or devices may not allow to measure dts and wall + * atomically. + */ +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + + +/** + * @} + */ + + +/** + * @defgroup lavf_misc Utility functions + * @ingroup libavf + * @{ + * + * Miscellaneous utility functions related to both muxing and demuxing + * (or neither). + */ + +/** + * Send a nice hexadecimal dump of a buffer to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump_log, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump(FILE *f, const uint8_t *buf, int size); + +/** + * Send a nice hexadecimal dump of a buffer to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size); + +/** + * Send a nice dump of a packet to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump2(FILE *f, AVPacket *pkt, int dump_payload, AVStream *st); + + +/** + * Send a nice dump of a packet to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump_log2(void *avcl, int level, AVPacket *pkt, int dump_payload, + AVStream *st); + +/** + * Get the AVCodecID for the given codec tag tag. + * If no codec id is found returns AV_CODEC_ID_NONE. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + */ +enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned int tag); + +/** + * Get the codec tag for the given codec id id. + * If no codec tag is found returns 0. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + */ +unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id); + +/** + * Get the codec tag for the given codec id. + * + * @param tags list of supported codec_id - codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec id that should be searched for in the list + * @param tag A pointer to the found tag + * @return 0 if id was not found in tags, > 0 if it was found + */ +int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id, + unsigned int *tag); + +int av_find_default_stream_index(AVFormatContext *s); + +/** + * Get the index for a specific timestamp. + * @param flags if AVSEEK_FLAG_BACKWARD then the returned index will correspond + * to the timestamp which is <= the requested one, if backward + * is 0, then it will be >= + * if AVSEEK_FLAG_ANY seek to any frame, only keyframes otherwise + * @return < 0 if no such timestamp could be found + */ +int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags); + +/** + * Add an index entry into a sorted list. Update the entry if the list + * already contains it. + * + * @param timestamp timestamp in the time base of the given stream + */ +int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, + int size, int distance, int flags); + + +/** + * Split a URL string into components. + * + * The pointers to buffers for storing individual components may be null, + * in order to ignore that component. Buffers for components not found are + * set to empty strings. If the port is not found, it is set to a negative + * value. + * + * @param proto the buffer for the protocol + * @param proto_size the size of the proto buffer + * @param authorization the buffer for the authorization + * @param authorization_size the size of the authorization buffer + * @param hostname the buffer for the host name + * @param hostname_size the size of the hostname buffer + * @param port_ptr a pointer to store the port number in + * @param path the buffer for the path + * @param path_size the size of the path buffer + * @param url the URL to split + */ +void av_url_split(char *proto, int proto_size, + char *authorization, int authorization_size, + char *hostname, int hostname_size, + int *port_ptr, + char *path, int path_size, + const char *url); + + +void av_dump_format(AVFormatContext *ic, + int index, + const char *url, + int is_output); + +/** + * Return in 'buf' the path with '%d' replaced by a number. + * + * Also handles the '%0nd' format where 'n' is the total number + * of digits and '%%'. + * + * @param buf destination buffer + * @param buf_size destination buffer size + * @param path numbered sequence string + * @param number frame number + * @return 0 if OK, -1 on format error + */ +int av_get_frame_filename(char *buf, int buf_size, + const char *path, int number); + +/** + * Check whether filename actually is a numbered sequence generator. + * + * @param filename possible numbered sequence string + * @return 1 if a valid numbered sequence string, 0 otherwise + */ +int av_filename_number_test(const char *filename); + +/** + * Generate an SDP for an RTP session. + * + * Note, this overwrites the id values of AVStreams in the muxer contexts + * for getting unique dynamic payload types. + * + * @param ac array of AVFormatContexts describing the RTP streams. If the + * array is composed by only one context, such context can contain + * multiple AVStreams (one AVStream per RTP stream). Otherwise, + * all the contexts in the array (an AVCodecContext per RTP stream) + * must contain only one AVStream. + * @param n_files number of AVCodecContexts contained in ac + * @param buf buffer where the SDP will be stored (must be allocated by + * the caller) + * @param size the size of the buffer + * @return 0 if OK, AVERROR_xxx on error + */ +int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size); + +/** + * Return a positive value if the given filename has one of the given + * extensions, 0 otherwise. + * + * @param extensions a comma-separated list of filename extensions + */ +int av_match_ext(const char *filename, const char *extensions); + +/** + * Test if the given container can store a codec. + * + * @param std_compliance standards compliance level, one of FF_COMPLIANCE_* + * + * @return 1 if codec with ID codec_id can be stored in ofmt, 0 if it cannot. + * A negative number if this information is not available. + */ +int avformat_query_codec(AVOutputFormat *ofmt, enum AVCodecID codec_id, int std_compliance); + +/** + * @defgroup riff_fourcc RIFF FourCCs + * @{ + * Get the tables mapping RIFF FourCCs to libavcodec AVCodecIDs. The tables are + * meant to be passed to av_codec_get_id()/av_codec_get_tag() as in the + * following code: + * @code + * uint32_t tag = MKTAG('H', '2', '6', '4'); + * const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 }; + * enum AVCodecID id = av_codec_get_id(table, tag); + * @endcode + */ +/** + * @return the table mapping RIFF FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_video_tags(void); +/** + * @return the table mapping RIFF FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_audio_tags(void); + +/** + * @} + */ + +/** + * Guess the sample aspect ratio of a frame, based on both the stream and the + * frame aspect ratio. + * + * Since the frame aspect ratio is set by the codec but the stream aspect ratio + * is set by the demuxer, these two may not be equal. This function tries to + * return the value that you should use if you would like to display the frame. + * + * Basic logic is to use the stream aspect ratio if it is set to something sane + * otherwise use the frame aspect ratio. This way a container setting, which is + * usually easy to modify can override the coded value in the frames. + * + * @param format the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame with the aspect ratio to be determined + * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea + */ +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); + +/** + * Guess the frame rate, based on both the container and codec information. + * + * @param ctx the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame for which the frame rate should be determined, may be NULL + * @return the guessed (valid) frame rate, 0/1 if no idea + */ +AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame); + +/** + * Check if the stream st contained in s is matched by the stream specifier + * spec. + * + * See the "stream specifiers" chapter in the documentation for the syntax + * of spec. + * + * @return >0 if st is matched by spec; + * 0 if st is not matched by spec; + * AVERROR code if spec is invalid + * + * @note A stream specifier can match several streams in the format. + */ +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec); + +int avformat_queue_attached_pictures(AVFormatContext *s); + + +/** + * @} + */ + +#endif /* AVFORMAT_AVFORMAT_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avi.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,38 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVI_H +#define AVFORMAT_AVI_H + +#define AVIF_HASINDEX 0x00000010 // Index at end of file? +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames? +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + +#define AVI_MAX_RIFF_SIZE 0x40000000LL +#define AVI_MASTER_INDEX_SIZE 256 +#define AVI_MAX_STREAM_COUNT 100 + +/* index flags */ +#define AVIIF_INDEX 0x10 + +#endif /* AVFORMAT_AVI_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avidec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avidec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1614 @@ +/* + * AVI demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/bswap.h" +#include "libavutil/opt.h" +#include "libavutil/dict.h" +#include "libavutil/avstring.h" +#include "libavutil/avassert.h" +#include "avformat.h" +#include "internal.h" +#include "avi.h" +#include "dv.h" +#include "riff.h" + +typedef struct AVIStream { + int64_t frame_offset; /* current frame (video) or byte (audio) counter + (used to compute the pts) */ + int remaining; + int packet_size; + + uint32_t scale; + uint32_t rate; + int sample_size; /* size of one sample (or packet) (in the rate/scale sense) in bytes */ + + int64_t cum_len; /* temporary storage (used during seek) */ + + int prefix; ///< normally 'd'<<8 + 'c' or 'w'<<8 + 'b' + int prefix_count; + uint32_t pal[256]; + int has_pal; + int dshow_block_align; ///< block align variable used to emulate bugs in the MS dshow demuxer + + AVFormatContext *sub_ctx; + AVPacket sub_pkt; + uint8_t *sub_buffer; + + int64_t seek_pos; +} AVIStream; + +typedef struct { + const AVClass *class; + int64_t riff_end; + int64_t movi_end; + int64_t fsize; + int64_t io_fsize; + int64_t movi_list; + int64_t last_pkt_pos; + int index_loaded; + int is_odml; + int non_interleaved; + int stream_index; + DVDemuxContext* dv_demux; + int odml_depth; + int use_odml; +#define MAX_ODML_DEPTH 1000 + int64_t dts_max; +} AVIContext; + + +static const AVOption options[] = { + { "use_odml", "use odml index", offsetof(AVIContext, use_odml), AV_OPT_TYPE_INT, {.i64 = 1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM}, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "avi", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + + +static const char avi_headers[][8] = { + { 'R', 'I', 'F', 'F', 'A', 'V', 'I', ' ' }, + { 'R', 'I', 'F', 'F', 'A', 'V', 'I', 'X' }, + { 'R', 'I', 'F', 'F', 'A', 'V', 'I', 0x19}, + { 'O', 'N', '2', ' ', 'O', 'N', '2', 'f' }, + { 'R', 'I', 'F', 'F', 'A', 'M', 'V', ' ' }, + { 0 } +}; + +static const AVMetadataConv avi_metadata_conv[] = { + { "strn", "title" }, + { 0 }, +}; + +static int avi_load_index(AVFormatContext *s); +static int guess_ni_flag(AVFormatContext *s); + +#define print_tag(str, tag, size) \ + av_dlog(NULL, "%s: tag=%c%c%c%c size=0x%x\n", \ + str, tag & 0xff, \ + (tag >> 8) & 0xff, \ + (tag >> 16) & 0xff, \ + (tag >> 24) & 0xff, \ + size) + +static inline int get_duration(AVIStream *ast, int len){ + if(ast->sample_size){ + return len; + }else if (ast->dshow_block_align){ + return (len + ast->dshow_block_align - 1)/ast->dshow_block_align; + }else + return 1; +} + +static int get_riff(AVFormatContext *s, AVIOContext *pb) +{ + AVIContext *avi = s->priv_data; + char header[8]; + int i; + + /* check RIFF header */ + avio_read(pb, header, 4); + avi->riff_end = avio_rl32(pb); /* RIFF chunk size */ + avi->riff_end += avio_tell(pb); /* RIFF chunk end */ + avio_read(pb, header+4, 4); + + for(i=0; avi_headers[i][0]; i++) + if(!memcmp(header, avi_headers[i], 8)) + break; + if(!avi_headers[i][0]) + return AVERROR_INVALIDDATA; + + if(header[7] == 0x19) + av_log(s, AV_LOG_INFO, "This file has been generated by a totally broken muxer.\n"); + + return 0; +} + +static int read_braindead_odml_indx(AVFormatContext *s, int frame_num){ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int longs_pre_entry= avio_rl16(pb); + int index_sub_type = avio_r8(pb); + int index_type = avio_r8(pb); + int entries_in_use = avio_rl32(pb); + int chunk_id = avio_rl32(pb); + int64_t base = avio_rl64(pb); + int stream_id= 10*((chunk_id&0xFF) - '0') + (((chunk_id>>8)&0xFF) - '0'); + AVStream *st; + AVIStream *ast; + int i; + int64_t last_pos= -1; + int64_t filesize= avi->fsize; + + av_dlog(s, "longs_pre_entry:%d index_type:%d entries_in_use:%d chunk_id:%X base:%16"PRIX64"\n", + longs_pre_entry,index_type, entries_in_use, chunk_id, base); + + if(stream_id >= s->nb_streams || stream_id < 0) + return AVERROR_INVALIDDATA; + st= s->streams[stream_id]; + ast = st->priv_data; + + if(index_sub_type) + return AVERROR_INVALIDDATA; + + avio_rl32(pb); + + if(index_type && longs_pre_entry != 2) + return AVERROR_INVALIDDATA; + if(index_type>1) + return AVERROR_INVALIDDATA; + + if(filesize > 0 && base >= filesize){ + av_log(s, AV_LOG_ERROR, "ODML index invalid\n"); + if(base>>32 == (base & 0xFFFFFFFF) && (base & 0xFFFFFFFF) < filesize && filesize <= 0xFFFFFFFF) + base &= 0xFFFFFFFF; + else + return AVERROR_INVALIDDATA; + } + + for(i=0; i= 0; + len &= 0x7FFFFFFF; + +#ifdef DEBUG_SEEK + av_log(s, AV_LOG_ERROR, "pos:%"PRId64", len:%X\n", pos, len); +#endif + if(url_feof(pb)) + return AVERROR_INVALIDDATA; + + if(last_pos == pos || pos == base - 8) + avi->non_interleaved= 1; + if(last_pos != pos && (len || !ast->sample_size)) + av_add_index_entry(st, pos, ast->cum_len, len, 0, key ? AVINDEX_KEYFRAME : 0); + + ast->cum_len += get_duration(ast, len); + last_pos= pos; + }else{ + int64_t offset, pos; + int duration; + offset = avio_rl64(pb); + avio_rl32(pb); /* size */ + duration = avio_rl32(pb); + + if(url_feof(pb)) + return AVERROR_INVALIDDATA; + + pos = avio_tell(pb); + + if(avi->odml_depth > MAX_ODML_DEPTH){ + av_log(s, AV_LOG_ERROR, "Too deeply nested ODML indexes\n"); + return AVERROR_INVALIDDATA; + } + + if(avio_seek(pb, offset+8, SEEK_SET) < 0) + return -1; + avi->odml_depth++; + read_braindead_odml_indx(s, frame_num); + avi->odml_depth--; + frame_num += duration; + + if(avio_seek(pb, pos, SEEK_SET) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to restore position after reading index\n"); + return -1; + } + + } + } + avi->index_loaded=2; + return 0; +} + +static void clean_index(AVFormatContext *s){ + int i; + int64_t j; + + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int n= st->nb_index_entries; + int max= ast->sample_size; + int64_t pos, size, ts; + + if(n != 1 || ast->sample_size==0) + continue; + + while(max < 1024) max+=max; + + pos= st->index_entries[0].pos; + size= st->index_entries[0].size; + ts= st->index_entries[0].timestamp; + + for(j=0; jpb; + char key[5] = {0}, *value; + + size += (size & 1); + + if (size == UINT_MAX) + return AVERROR(EINVAL); + value = av_malloc(size+1); + if (!value) + return AVERROR(ENOMEM); + avio_read(pb, value, size); + value[size]=0; + + AV_WL32(key, tag); + + return av_dict_set(st ? &st->metadata : &s->metadata, key, value, + AV_DICT_DONT_STRDUP_VAL); +} + +static const char months[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +static void avi_metadata_creation_time(AVDictionary **metadata, char *date) +{ + char month[4], time[9], buffer[64]; + int i, day, year; + /* parse standard AVI date format (ie. "Mon Mar 10 15:04:43 2003") */ + if (sscanf(date, "%*3s%*[ ]%3s%*[ ]%2d%*[ ]%8s%*[ ]%4d", + month, &day, time, &year) == 4) { + for (i=0; i<12; i++) + if (!av_strcasecmp(month, months[i])) { + snprintf(buffer, sizeof(buffer), "%.4d-%.2d-%.2d %s", + year, i+1, day, time); + av_dict_set(metadata, "creation_time", buffer, 0); + } + } else if (date[4] == '/' && date[7] == '/') { + date[4] = date[7] = '-'; + av_dict_set(metadata, "creation_time", date, 0); + } +} + +static void avi_read_nikon(AVFormatContext *s, uint64_t end) +{ + while (avio_tell(s->pb) < end) { + uint32_t tag = avio_rl32(s->pb); + uint32_t size = avio_rl32(s->pb); + switch (tag) { + case MKTAG('n', 'c', 't', 'g'): { /* Nikon Tags */ + uint64_t tag_end = avio_tell(s->pb) + size; + while (avio_tell(s->pb) < tag_end) { + uint16_t tag = avio_rl16(s->pb); + uint16_t size = avio_rl16(s->pb); + const char *name = NULL; + char buffer[64] = {0}; + size -= avio_read(s->pb, buffer, + FFMIN(size, sizeof(buffer)-1)); + switch (tag) { + case 0x03: name = "maker"; break; + case 0x04: name = "model"; break; + case 0x13: name = "creation_time"; + if (buffer[4] == ':' && buffer[7] == ':') + buffer[4] = buffer[7] = '-'; + break; + } + if (name) + av_dict_set(&s->metadata, name, buffer, 0); + avio_skip(s->pb, size); + } + break; + } + default: + avio_skip(s->pb, size); + break; + } + } +} + +static int avi_read_header(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + unsigned int tag, tag1, handler; + int codec_type, stream_index, frame_period; + unsigned int size; + int i; + AVStream *st; + AVIStream *ast = NULL; + int avih_width=0, avih_height=0; + int amv_file_format=0; + uint64_t list_end = 0; + int ret; + + avi->stream_index= -1; + + ret = get_riff(s, pb); + if (ret < 0) + return ret; + + av_log(avi, AV_LOG_DEBUG, "use odml:%d\n", avi->use_odml); + + avi->io_fsize = avi->fsize = avio_size(pb); + if(avi->fsize<=0 || avi->fsize < avi->riff_end) + avi->fsize= avi->riff_end == 8 ? INT64_MAX : avi->riff_end; + + /* first list tag */ + stream_index = -1; + codec_type = -1; + frame_period = 0; + for(;;) { + if (url_feof(pb)) + goto fail; + tag = avio_rl32(pb); + size = avio_rl32(pb); + + print_tag("tag", tag, size); + + switch(tag) { + case MKTAG('L', 'I', 'S', 'T'): + list_end = avio_tell(pb) + size; + /* Ignored, except at start of video packets. */ + tag1 = avio_rl32(pb); + + print_tag("list", tag1, 0); + + if (tag1 == MKTAG('m', 'o', 'v', 'i')) { + avi->movi_list = avio_tell(pb) - 4; + if(size) avi->movi_end = avi->movi_list + size + (size & 1); + else avi->movi_end = avi->fsize; + av_dlog(NULL, "movi end=%"PRIx64"\n", avi->movi_end); + goto end_of_header; + } + else if (tag1 == MKTAG('I', 'N', 'F', 'O')) + ff_read_riff_info(s, size - 4); + else if (tag1 == MKTAG('n', 'c', 'd', 't')) + avi_read_nikon(s, list_end); + + break; + case MKTAG('I', 'D', 'I', 'T'): { + unsigned char date[64] = {0}; + size += (size & 1); + size -= avio_read(pb, date, FFMIN(size, sizeof(date)-1)); + avio_skip(pb, size); + avi_metadata_creation_time(&s->metadata, date); + break; + } + case MKTAG('d', 'm', 'l', 'h'): + avi->is_odml = 1; + avio_skip(pb, size + (size & 1)); + break; + case MKTAG('a', 'm', 'v', 'h'): + amv_file_format=1; + case MKTAG('a', 'v', 'i', 'h'): + /* AVI header */ + /* using frame_period is bad idea */ + frame_period = avio_rl32(pb); + avio_rl32(pb); /* max. bytes per second */ + avio_rl32(pb); + avi->non_interleaved |= avio_rl32(pb) & AVIF_MUSTUSEINDEX; + + avio_skip(pb, 2 * 4); + avio_rl32(pb); + avio_rl32(pb); + avih_width=avio_rl32(pb); + avih_height=avio_rl32(pb); + + avio_skip(pb, size - 10 * 4); + break; + case MKTAG('s', 't', 'r', 'h'): + /* stream header */ + + tag1 = avio_rl32(pb); + handler = avio_rl32(pb); /* codec tag */ + + if(tag1 == MKTAG('p', 'a', 'd', 's')){ + avio_skip(pb, size - 8); + break; + }else{ + stream_index++; + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + + st->id = stream_index; + ast = av_mallocz(sizeof(AVIStream)); + if (!ast) + goto fail; + st->priv_data = ast; + } + if(amv_file_format) + tag1 = stream_index ? MKTAG('a','u','d','s') : MKTAG('v','i','d','s'); + + print_tag("strh", tag1, -1); + + if(tag1 == MKTAG('i', 'a', 'v', 's') || tag1 == MKTAG('i', 'v', 'a', 's')){ + int64_t dv_dur; + + /* + * After some consideration -- I don't think we + * have to support anything but DV in type1 AVIs. + */ + if (s->nb_streams != 1) + goto fail; + + if (handler != MKTAG('d', 'v', 's', 'd') && + handler != MKTAG('d', 'v', 'h', 'd') && + handler != MKTAG('d', 'v', 's', 'l')) + goto fail; + + ast = s->streams[0]->priv_data; + av_freep(&s->streams[0]->codec->extradata); + av_freep(&s->streams[0]->codec); + if (s->streams[0]->info) + av_freep(&s->streams[0]->info->duration_error); + av_freep(&s->streams[0]->info); + av_freep(&s->streams[0]); + s->nb_streams = 0; + if (CONFIG_DV_DEMUXER) { + avi->dv_demux = avpriv_dv_init_demux(s); + if (!avi->dv_demux) + goto fail; + } + s->streams[0]->priv_data = ast; + avio_skip(pb, 3 * 4); + ast->scale = avio_rl32(pb); + ast->rate = avio_rl32(pb); + avio_skip(pb, 4); /* start time */ + + dv_dur = avio_rl32(pb); + if (ast->scale > 0 && ast->rate > 0 && dv_dur > 0) { + dv_dur *= AV_TIME_BASE; + s->duration = av_rescale(dv_dur, ast->scale, ast->rate); + } + /* + * else, leave duration alone; timing estimation in utils.c + * will make a guess based on bitrate. + */ + + stream_index = s->nb_streams - 1; + avio_skip(pb, size - 9*4); + break; + } + + av_assert0(stream_index < s->nb_streams); + st->codec->stream_codec_tag= handler; + + avio_rl32(pb); /* flags */ + avio_rl16(pb); /* priority */ + avio_rl16(pb); /* language */ + avio_rl32(pb); /* initial frame */ + ast->scale = avio_rl32(pb); + ast->rate = avio_rl32(pb); + if(!(ast->scale && ast->rate)){ + av_log(s, AV_LOG_WARNING, "scale/rate is %u/%u which is invalid. (This file has been generated by broken software.)\n", ast->scale, ast->rate); + if(frame_period){ + ast->rate = 1000000; + ast->scale = frame_period; + }else{ + ast->rate = 25; + ast->scale = 1; + } + } + avpriv_set_pts_info(st, 64, ast->scale, ast->rate); + + ast->cum_len=avio_rl32(pb); /* start */ + st->nb_frames = avio_rl32(pb); + + st->start_time = 0; + avio_rl32(pb); /* buffer size */ + avio_rl32(pb); /* quality */ + if (ast->cum_len*ast->scale/ast->rate > 3600) { + av_log(s, AV_LOG_ERROR, "crazy start time, iam scared, giving up\n"); + return AVERROR_INVALIDDATA; + } + ast->sample_size = avio_rl32(pb); /* sample ssize */ + ast->cum_len *= FFMAX(1, ast->sample_size); + av_dlog(s, "%"PRIu32" %"PRIu32" %d\n", + ast->rate, ast->scale, ast->sample_size); + + switch(tag1) { + case MKTAG('v', 'i', 'd', 's'): + codec_type = AVMEDIA_TYPE_VIDEO; + + ast->sample_size = 0; + break; + case MKTAG('a', 'u', 'd', 's'): + codec_type = AVMEDIA_TYPE_AUDIO; + break; + case MKTAG('t', 'x', 't', 's'): + codec_type = AVMEDIA_TYPE_SUBTITLE; + break; + case MKTAG('d', 'a', 't', 's'): + codec_type = AVMEDIA_TYPE_DATA; + break; + default: + av_log(s, AV_LOG_INFO, "unknown stream type %X\n", tag1); + } + if(ast->sample_size == 0) { + st->duration = st->nb_frames; + if (st->duration > 0 && avi->io_fsize > 0 && avi->riff_end > avi->io_fsize) { + av_log(s, AV_LOG_DEBUG, "File is truncated adjusting duration\n"); + st->duration = av_rescale(st->duration, avi->io_fsize, avi->riff_end); + } + } + ast->frame_offset= ast->cum_len; + avio_skip(pb, size - 12 * 4); + break; + case MKTAG('s', 't', 'r', 'f'): + /* stream header */ + if (!size) + break; + if (stream_index >= (unsigned)s->nb_streams || avi->dv_demux) { + avio_skip(pb, size); + } else { + uint64_t cur_pos = avio_tell(pb); + unsigned esize; + if (cur_pos < list_end) + size = FFMIN(size, list_end - cur_pos); + st = s->streams[stream_index]; + switch(codec_type) { + case AVMEDIA_TYPE_VIDEO: + if(amv_file_format){ + st->codec->width=avih_width; + st->codec->height=avih_height; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_AMV; + avio_skip(pb, size); + break; + } + tag1 = ff_get_bmp_header(pb, st, &esize); + + if (tag1 == MKTAG('D', 'X', 'S', 'B') || tag1 == MKTAG('D','X','S','A')) { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_tag = tag1; + st->codec->codec_id = AV_CODEC_ID_XSUB; + break; + } + + if(size > 10*4 && size<(1<<30) && size < avi->fsize){ + if(esize == size-1 && (esize&1)) st->codec->extradata_size= esize - 10*4; + else st->codec->extradata_size= size - 10*4; + st->codec->extradata= av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) { + st->codec->extradata_size= 0; + return AVERROR(ENOMEM); + } + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + } + + if(st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly + avio_r8(pb); + + /* Extract palette from extradata if bpp <= 8. */ + /* This code assumes that extradata contains only palette. */ + /* This is true for all paletted codecs implemented in FFmpeg. */ + if (st->codec->extradata_size && (st->codec->bits_per_coded_sample <= 8)) { + int pal_size = (1 << st->codec->bits_per_coded_sample) << 2; + const uint8_t *pal_src; + + pal_size = FFMIN(pal_size, st->codec->extradata_size); + pal_src = st->codec->extradata + st->codec->extradata_size - pal_size; + for (i = 0; i < pal_size/4; i++) + ast->pal[i] = 0xFFU<<24 | AV_RL32(pal_src+4*i); + ast->has_pal = 1; + } + + print_tag("video", tag1, 0); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = tag1; + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); + st->need_parsing = AVSTREAM_PARSE_HEADERS; // This is needed to get the pict type which is necessary for generating correct pts. + + if(st->codec->codec_tag==0 && st->codec->height > 0 && st->codec->extradata_size < 1U<<30){ + st->codec->extradata_size+= 9; + st->codec->extradata= av_realloc_f(st->codec->extradata, 1, st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if(st->codec->extradata) + memcpy(st->codec->extradata + st->codec->extradata_size - 9, "BottomUp", 9); + } + st->codec->height= FFABS(st->codec->height); + +// avio_skip(pb, size - 5 * 4); + break; + case AVMEDIA_TYPE_AUDIO: + ret = ff_get_wav_header(pb, st->codec, size); + if (ret < 0) + return ret; + ast->dshow_block_align= st->codec->block_align; + if(ast->sample_size && st->codec->block_align && ast->sample_size != st->codec->block_align){ + av_log(s, AV_LOG_WARNING, "sample size (%d) != block align (%d)\n", ast->sample_size, st->codec->block_align); + ast->sample_size= st->codec->block_align; + } + if (size&1) /* 2-aligned (fix for Stargate SG-1 - 3x18 - Shades of Grey.avi) */ + avio_skip(pb, 1); + /* Force parsing as several audio frames can be in + * one packet and timestamps refer to packet start. */ + st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS; + /* ADTS header is in extradata, AAC without header must be + * stored as exact frames. Parser not needed and it will + * fail. */ + if (st->codec->codec_id == AV_CODEC_ID_AAC && st->codec->extradata_size) + st->need_parsing = AVSTREAM_PARSE_NONE; + /* AVI files with Xan DPCM audio (wrongly) declare PCM + * audio in the header but have Axan as stream_code_tag. */ + if (st->codec->stream_codec_tag == AV_RL32("Axan")){ + st->codec->codec_id = AV_CODEC_ID_XAN_DPCM; + st->codec->codec_tag = 0; + ast->dshow_block_align = 0; + } + if (amv_file_format){ + st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_AMV; + ast->dshow_block_align = 0; + } + if(st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align <= 4 && ast->dshow_block_align) { + av_log(s, AV_LOG_DEBUG, "overriding invalid dshow_block_align of %d\n", ast->dshow_block_align); + ast->dshow_block_align = 0; + } + if(st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 1024 && ast->sample_size == 1024 || + st->codec->codec_id == AV_CODEC_ID_AAC && ast->dshow_block_align == 4096 && ast->sample_size == 4096 || + st->codec->codec_id == AV_CODEC_ID_MP3 && ast->dshow_block_align == 1152 && ast->sample_size == 1152) { + av_log(s, AV_LOG_DEBUG, "overriding sample_size\n"); + ast->sample_size = 0; + } + break; + case AVMEDIA_TYPE_SUBTITLE: + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->request_probe= 1; + avio_skip(pb, size); + break; + default: + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id= AV_CODEC_ID_NONE; + st->codec->codec_tag= 0; + avio_skip(pb, size); + break; + } + } + break; + case MKTAG('s', 't', 'r', 'd'): + if (stream_index >= (unsigned)s->nb_streams || s->streams[stream_index]->codec->extradata_size) { + avio_skip(pb, size); + } else { + uint64_t cur_pos = avio_tell(pb); + if (cur_pos < list_end) + size = FFMIN(size, list_end - cur_pos); + st = s->streams[stream_index]; + + if(size<(1<<30)){ + st->codec->extradata_size= size; + st->codec->extradata= av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) { + st->codec->extradata_size= 0; + return AVERROR(ENOMEM); + } + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + } + + if(st->codec->extradata_size & 1) //FIXME check if the encoder really did this correctly + avio_r8(pb); + } + break; + case MKTAG('i', 'n', 'd', 'x'): + i= avio_tell(pb); + if(pb->seekable && !(s->flags & AVFMT_FLAG_IGNIDX) && avi->use_odml && + read_braindead_odml_indx(s, 0) < 0 && (s->error_recognition & AV_EF_EXPLODE)) + goto fail; + avio_seek(pb, i+size, SEEK_SET); + break; + case MKTAG('v', 'p', 'r', 'p'): + if(stream_index < (unsigned)s->nb_streams && size > 9*4){ + AVRational active, active_aspect; + + st = s->streams[stream_index]; + avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + + active_aspect.den= avio_rl16(pb); + active_aspect.num= avio_rl16(pb); + active.num = avio_rl32(pb); + active.den = avio_rl32(pb); + avio_rl32(pb); //nbFieldsPerFrame + + if(active_aspect.num && active_aspect.den && active.num && active.den){ + st->sample_aspect_ratio= av_div_q(active_aspect, active); + av_dlog(s, "vprp %d/%d %d/%d\n", + active_aspect.num, active_aspect.den, + active.num, active.den); + } + size -= 9*4; + } + avio_skip(pb, size); + break; + case MKTAG('s', 't', 'r', 'n'): + if(s->nb_streams){ + ret = avi_read_tag(s, s->streams[s->nb_streams-1], tag, size); + if (ret < 0) + return ret; + break; + } + default: + if(size > 1000000){ + av_log(s, AV_LOG_ERROR, "Something went wrong during header parsing, " + "I will ignore it and try to continue anyway.\n"); + if (s->error_recognition & AV_EF_EXPLODE) + goto fail; + avi->movi_list = avio_tell(pb) - 4; + avi->movi_end = avi->fsize; + goto end_of_header; + } + /* skip tag */ + size += (size & 1); + avio_skip(pb, size); + break; + } + } + end_of_header: + /* check stream number */ + if (stream_index != s->nb_streams - 1) { + fail: + return AVERROR_INVALIDDATA; + } + + if(!avi->index_loaded && pb->seekable) + avi_load_index(s); + avi->index_loaded |= 1; + avi->non_interleaved |= guess_ni_flag(s) | (s->flags & AVFMT_FLAG_SORT_DTS); + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + if(st->nb_index_entries) + break; + } + // DV-in-AVI cannot be non-interleaved, if set this must be + // a mis-detection. + if(avi->dv_demux) + avi->non_interleaved=0; + if(i==s->nb_streams && avi->non_interleaved) { + av_log(s, AV_LOG_WARNING, "non-interleaved AVI without index, switching to interleaved\n"); + avi->non_interleaved=0; + } + + if(avi->non_interleaved) { + av_log(s, AV_LOG_INFO, "non-interleaved AVI\n"); + clean_index(s); + } + + ff_metadata_conv_ctx(s, NULL, avi_metadata_conv); + ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv); + + return 0; +} + +static int read_gab2_sub(AVStream *st, AVPacket *pkt) { + if (pkt->data && !strcmp(pkt->data, "GAB2") && AV_RL16(pkt->data+5) == 2) { + uint8_t desc[256]; + int score = AVPROBE_SCORE_MAX / 2, ret; + AVIStream *ast = st->priv_data; + AVInputFormat *sub_demuxer; + AVRational time_base; + AVIOContext *pb = avio_alloc_context( pkt->data + 7, + pkt->size - 7, + 0, NULL, NULL, NULL, NULL); + AVProbeData pd; + unsigned int desc_len = avio_rl32(pb); + + if (desc_len > pb->buf_end - pb->buf_ptr) + goto error; + + ret = avio_get_str16le(pb, desc_len, desc, sizeof(desc)); + avio_skip(pb, desc_len - ret); + if (*desc) + av_dict_set(&st->metadata, "title", desc, 0); + + avio_rl16(pb); /* flags? */ + avio_rl32(pb); /* data size */ + + pd = (AVProbeData) { .buf = pb->buf_ptr, .buf_size = pb->buf_end - pb->buf_ptr }; + if (!(sub_demuxer = av_probe_input_format2(&pd, 1, &score))) + goto error; + + if (!(ast->sub_ctx = avformat_alloc_context())) + goto error; + + ast->sub_ctx->pb = pb; + if (!avformat_open_input(&ast->sub_ctx, "", sub_demuxer, NULL)) { + ff_read_packet(ast->sub_ctx, &ast->sub_pkt); + *st->codec = *ast->sub_ctx->streams[0]->codec; + ast->sub_ctx->streams[0]->codec->extradata = NULL; + time_base = ast->sub_ctx->streams[0]->time_base; + avpriv_set_pts_info(st, 64, time_base.num, time_base.den); + } + ast->sub_buffer = pkt->data; + memset(pkt, 0, sizeof(*pkt)); + return 1; +error: + av_freep(&pb); + } + return 0; +} + +static AVStream *get_subtitle_pkt(AVFormatContext *s, AVStream *next_st, + AVPacket *pkt) +{ + AVIStream *ast, *next_ast = next_st->priv_data; + int64_t ts, next_ts, ts_min = INT64_MAX; + AVStream *st, *sub_st = NULL; + int i; + + next_ts = av_rescale_q(next_ast->frame_offset, next_st->time_base, + AV_TIME_BASE_Q); + + for (i=0; inb_streams; i++) { + st = s->streams[i]; + ast = st->priv_data; + if (st->discard < AVDISCARD_ALL && ast && ast->sub_pkt.data) { + ts = av_rescale_q(ast->sub_pkt.dts, st->time_base, AV_TIME_BASE_Q); + if (ts <= next_ts && ts < ts_min) { + ts_min = ts; + sub_st = st; + } + } + } + + if (sub_st) { + ast = sub_st->priv_data; + *pkt = ast->sub_pkt; + pkt->stream_index = sub_st->index; + if (ff_read_packet(ast->sub_ctx, &ast->sub_pkt) < 0) + ast->sub_pkt.data = NULL; + } + return sub_st; +} + +static int get_stream_idx(int *d){ + if( d[0] >= '0' && d[0] <= '9' + && d[1] >= '0' && d[1] <= '9'){ + return (d[0] - '0') * 10 + (d[1] - '0'); + }else{ + return 100; //invalid stream ID + } +} + +/** + * + * @param exit_early set to 1 to just gather packet position without making the changes needed to actually read & return the packet + */ +static int avi_sync(AVFormatContext *s, int exit_early) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int n; + unsigned int d[8]; + unsigned int size; + int64_t i, sync; + +start_sync: + memset(d, -1, sizeof(d)); + for(i=sync=avio_tell(pb); !url_feof(pb); i++) { + int j; + + for(j=0; j<7; j++) + d[j]= d[j+1]; + d[7]= avio_r8(pb); + + size= d[4] + (d[5]<<8) + (d[6]<<16) + (d[7]<<24); + + n= get_stream_idx(d+2); + av_dlog(s, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], i, size, n); + if(i*(avi->io_fsize>0) + (uint64_t)size > avi->fsize || d[0] > 127) + continue; + + //parse ix## + if( (d[0] == 'i' && d[1] == 'x' && n < s->nb_streams) + //parse JUNK + ||(d[0] == 'J' && d[1] == 'U' && d[2] == 'N' && d[3] == 'K') + ||(d[0] == 'i' && d[1] == 'd' && d[2] == 'x' && d[3] == '1')){ + avio_skip(pb, size); + goto start_sync; + } + + //parse stray LIST + if(d[0] == 'L' && d[1] == 'I' && d[2] == 'S' && d[3] == 'T'){ + avio_skip(pb, 4); + goto start_sync; + } + + n= get_stream_idx(d); + + if(!((i-avi->last_pkt_pos)&1) && get_stream_idx(d+1) < s->nb_streams) + continue; + + //detect ##ix chunk and skip + if(d[2] == 'i' && d[3] == 'x' && n < s->nb_streams){ + avio_skip(pb, size); + goto start_sync; + } + + //parse ##dc/##wb + if(n < s->nb_streams){ + AVStream *st; + AVIStream *ast; + st = s->streams[n]; + ast = st->priv_data; + + if (!ast) { + av_log(s, AV_LOG_WARNING, "Skiping foreign stream %d packet\n", n); + continue; + } + + if(s->nb_streams>=2){ + AVStream *st1 = s->streams[1]; + AVIStream *ast1= st1->priv_data; + //workaround for broken small-file-bug402.avi + if( d[2] == 'w' && d[3] == 'b' + && n==0 + && st ->codec->codec_type == AVMEDIA_TYPE_VIDEO + && st1->codec->codec_type == AVMEDIA_TYPE_AUDIO + && ast->prefix == 'd'*256+'c' + && (d[2]*256+d[3] == ast1->prefix || !ast1->prefix_count) + ){ + n=1; + st = st1; + ast = ast1; + av_log(s, AV_LOG_WARNING, "Invalid stream + prefix combination, assuming audio.\n"); + } + } + + + if( (st->discard >= AVDISCARD_DEFAULT && size==0) + /*|| (st->discard >= AVDISCARD_NONKEY && !(pkt->flags & AV_PKT_FLAG_KEY))*/ //FIXME needs a little reordering + || st->discard >= AVDISCARD_ALL){ + if (!exit_early) { + ast->frame_offset += get_duration(ast, size); + } + avio_skip(pb, size); + goto start_sync; + } + + if (d[2] == 'p' && d[3] == 'c' && size<=4*256+4) { + int k = avio_r8(pb); + int last = (k + avio_r8(pb) - 1) & 0xFF; + + avio_rl16(pb); //flags + + for (; k <= last; k++) + ast->pal[k] = 0xFFU<<24 | avio_rb32(pb)>>8;// b + (g << 8) + (r << 16); + ast->has_pal= 1; + goto start_sync; + } else if( ((ast->prefix_count<5 || sync+9 > i) && d[2]<128 && d[3]<128) || + d[2]*256+d[3] == ast->prefix /*|| + (d[2] == 'd' && d[3] == 'c') || + (d[2] == 'w' && d[3] == 'b')*/) { + + if (exit_early) + return 0; + if(d[2]*256+d[3] == ast->prefix) + ast->prefix_count++; + else{ + ast->prefix= d[2]*256+d[3]; + ast->prefix_count= 0; + } + + avi->stream_index= n; + ast->packet_size= size + 8; + ast->remaining= size; + + if(size || !ast->sample_size){ + uint64_t pos= avio_tell(pb) - 8; + if(!st->index_entries || !st->nb_index_entries || st->index_entries[st->nb_index_entries - 1].pos < pos){ + av_add_index_entry(st, pos, ast->frame_offset, size, 0, AVINDEX_KEYFRAME); + } + } + return 0; + } + } + } + + if(pb->error) + return pb->error; + return AVERROR_EOF; +} + +static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int err; +#if FF_API_DESTRUCT_PACKET + void* dstr; +#endif + + if (CONFIG_DV_DEMUXER && avi->dv_demux) { + int size = avpriv_dv_get_packet(avi->dv_demux, pkt); + if (size >= 0) + return size; + } + + if(avi->non_interleaved){ + int best_stream_index = 0; + AVStream *best_st= NULL; + AVIStream *best_ast; + int64_t best_ts= INT64_MAX; + int i; + + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int64_t ts= ast->frame_offset; + int64_t last_ts; + + if(!st->nb_index_entries) + continue; + + last_ts = st->index_entries[st->nb_index_entries - 1].timestamp; + if(!ast->remaining && ts > last_ts) + continue; + + ts = av_rescale_q(ts, st->time_base, (AVRational){FFMAX(1, ast->sample_size), AV_TIME_BASE}); + + av_dlog(s, "%"PRId64" %d/%d %"PRId64"\n", ts, + st->time_base.num, st->time_base.den, ast->frame_offset); + if(ts < best_ts){ + best_ts= ts; + best_st= st; + best_stream_index= i; + } + } + if(!best_st) + return AVERROR_EOF; + + best_ast = best_st->priv_data; + best_ts = best_ast->frame_offset; + if(best_ast->remaining) + i= av_index_search_timestamp(best_st, best_ts, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); + else{ + i= av_index_search_timestamp(best_st, best_ts, AVSEEK_FLAG_ANY); + if(i>=0) + best_ast->frame_offset= best_st->index_entries[i].timestamp; + } + + if(i>=0){ + int64_t pos= best_st->index_entries[i].pos; + pos += best_ast->packet_size - best_ast->remaining; + if(avio_seek(s->pb, pos + 8, SEEK_SET) < 0) + return AVERROR_EOF; + + av_assert0(best_ast->remaining <= best_ast->packet_size); + + avi->stream_index= best_stream_index; + if(!best_ast->remaining) + best_ast->packet_size= + best_ast->remaining= best_st->index_entries[i].size; + } + else + return AVERROR_EOF; + } + +resync: + if(avi->stream_index >= 0){ + AVStream *st= s->streams[ avi->stream_index ]; + AVIStream *ast= st->priv_data; + int size, err; + + if(get_subtitle_pkt(s, st, pkt)) + return 0; + + if(ast->sample_size <= 1) // minorityreport.AVI block_align=1024 sample_size=1 IMA-ADPCM + size= INT_MAX; + else if(ast->sample_size < 32) + // arbitrary multiplier to avoid tiny packets for raw PCM data + size= 1024*ast->sample_size; + else + size= ast->sample_size; + + if(size > ast->remaining) + size= ast->remaining; + avi->last_pkt_pos= avio_tell(pb); + err= av_get_packet(pb, pkt, size); + if(err<0) + return err; + size = err; + + if(ast->has_pal && pkt->size<(unsigned)INT_MAX/2){ + uint8_t *pal; + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + if(!pal){ + av_log(s, AV_LOG_ERROR, "Failed to allocate data for palette\n"); + }else{ + memcpy(pal, ast->pal, AVPALETTE_SIZE); + ast->has_pal = 0; + } + } + + if (CONFIG_DV_DEMUXER && avi->dv_demux) { + AVBufferRef *avbuf = pkt->buf; +#if FF_API_DESTRUCT_PACKET + dstr = pkt->destruct; +#endif + size = avpriv_dv_produce_packet(avi->dv_demux, pkt, + pkt->data, pkt->size, pkt->pos); +#if FF_API_DESTRUCT_PACKET + pkt->destruct = dstr; +#endif + pkt->buf = avbuf; + pkt->flags |= AV_PKT_FLAG_KEY; + if (size < 0) + av_free_packet(pkt); + } else if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE + && !st->codec->codec_tag && read_gab2_sub(st, pkt)) { + ast->frame_offset++; + avi->stream_index = -1; + ast->remaining = 0; + goto resync; + } else { + /* XXX: How to handle B-frames in AVI? */ + pkt->dts = ast->frame_offset; +// pkt->dts += ast->start; + if(ast->sample_size) + pkt->dts /= ast->sample_size; + av_dlog(s, "dts:%"PRId64" offset:%"PRId64" %d/%d smpl_siz:%d base:%d st:%d size:%d\n", + pkt->dts, ast->frame_offset, ast->scale, ast->rate, + ast->sample_size, AV_TIME_BASE, avi->stream_index, size); + pkt->stream_index = avi->stream_index; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + AVIndexEntry *e; + int index; + av_assert0(st->index_entries); + + index= av_index_search_timestamp(st, ast->frame_offset, 0); + e= &st->index_entries[index]; + + if(index >= 0 && e->timestamp == ast->frame_offset){ + if (index == st->nb_index_entries-1){ + int key=1; + int i; + uint32_t state=-1; + for(i=0; icodec->codec_id == AV_CODEC_ID_MPEG4){ + if(state == 0x1B6){ + key= !(pkt->data[i]&0xC0); + break; + } + }else + break; + state= (state<<8) + pkt->data[i]; + } + if(!key) + e->flags &= ~AVINDEX_KEYFRAME; + } + if (e->flags & AVINDEX_KEYFRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + } + } else { + pkt->flags |= AV_PKT_FLAG_KEY; + } + ast->frame_offset += get_duration(ast, pkt->size); + } + ast->remaining -= err; + if(!ast->remaining){ + avi->stream_index= -1; + ast->packet_size= 0; + } + + if(!avi->non_interleaved && pkt->pos >= 0 && ast->seek_pos > pkt->pos){ + av_free_packet(pkt); + goto resync; + } + ast->seek_pos= 0; + + if(!avi->non_interleaved && st->nb_index_entries>1 && avi->index_loaded>1){ + int64_t dts= av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q); + + if(avi->dts_max - dts > 2*AV_TIME_BASE){ + avi->non_interleaved= 1; + av_log(s, AV_LOG_INFO, "Switching to NI mode, due to poor interleaving\n"); + }else if(avi->dts_max < dts) + avi->dts_max = dts; + } + + return 0; + } + + if ((err = avi_sync(s, 0)) < 0) + return err; + goto resync; +} + +/* XXX: We make the implicit supposition that the positions are sorted + for each stream. */ +static int avi_read_idx1(AVFormatContext *s, int size) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int nb_index_entries, i; + AVStream *st; + AVIStream *ast; + unsigned int index, tag, flags, pos, len, first_packet = 1; + unsigned last_pos= -1; + unsigned last_idx= -1; + int64_t idx1_pos, first_packet_pos = 0, data_offset = 0; + int anykey = 0; + + nb_index_entries = size / 16; + if (nb_index_entries <= 0) + return AVERROR_INVALIDDATA; + + idx1_pos = avio_tell(pb); + avio_seek(pb, avi->movi_list+4, SEEK_SET); + if (avi_sync(s, 1) == 0) { + first_packet_pos = avio_tell(pb) - 8; + } + avi->stream_index = -1; + avio_seek(pb, idx1_pos, SEEK_SET); + + if (s->nb_streams == 1 && s->streams[0]->codec->codec_tag == AV_RL32("MMES")){ + first_packet_pos = 0; + data_offset = avi->movi_list; + } + + /* Read the entries and sort them in each stream component. */ + for(i = 0; i < nb_index_entries; i++) { + if(url_feof(pb)) + return -1; + + tag = avio_rl32(pb); + flags = avio_rl32(pb); + pos = avio_rl32(pb); + len = avio_rl32(pb); + av_dlog(s, "%d: tag=0x%x flags=0x%x pos=0x%x len=%d/", + i, tag, flags, pos, len); + + index = ((tag & 0xff) - '0') * 10; + index += ((tag >> 8) & 0xff) - '0'; + if (index >= s->nb_streams) + continue; + st = s->streams[index]; + ast = st->priv_data; + + if(first_packet && first_packet_pos && len) { + data_offset = first_packet_pos - pos; + first_packet = 0; + } + pos += data_offset; + + av_dlog(s, "%d cum_len=%"PRId64"\n", len, ast->cum_len); + + // even if we have only a single stream, we should + // switch to non-interleaved to get correct timestamps + if(last_pos == pos) + avi->non_interleaved= 1; + if(last_idx != pos && len) { + av_add_index_entry(st, pos, ast->cum_len, len, 0, (flags&AVIIF_INDEX) ? AVINDEX_KEYFRAME : 0); + last_idx= pos; + } + ast->cum_len += get_duration(ast, len); + last_pos= pos; + anykey |= flags&AVIIF_INDEX; + } + if (!anykey) { + for (index = 0; index < s->nb_streams; index++) { + st = s->streams[index]; + if (st->nb_index_entries) + st->index_entries[0].flags |= AVINDEX_KEYFRAME; + } + } + return 0; +} + +static int guess_ni_flag(AVFormatContext *s){ + int i; + int64_t last_start=0; + int64_t first_end= INT64_MAX; + int64_t oldpos= avio_tell(s->pb); + int *idx; + int64_t min_pos, pos; + + for(i=0; inb_streams; i++){ + AVStream *st = s->streams[i]; + int n= st->nb_index_entries; + unsigned int size; + + if(n <= 0) + continue; + + if(n >= 2){ + int64_t pos= st->index_entries[0].pos; + avio_seek(s->pb, pos + 4, SEEK_SET); + size= avio_rl32(s->pb); + if(pos + size > st->index_entries[1].pos) + last_start= INT64_MAX; + } + + if(st->index_entries[0].pos > last_start) + last_start= st->index_entries[0].pos; + if(st->index_entries[n-1].pos < first_end) + first_end= st->index_entries[n-1].pos; + } + avio_seek(s->pb, oldpos, SEEK_SET); + if (last_start > first_end) + return 1; + idx= av_mallocz(sizeof(*idx) * s->nb_streams); + for (min_pos=pos=0; min_pos!=INT64_MAX; pos= min_pos+1LU) { + int64_t max_dts = INT64_MIN/2, min_dts= INT64_MAX/2; + min_pos = INT64_MAX; + + for (i=0; inb_streams; i++) { + AVStream *st = s->streams[i]; + int n= st->nb_index_entries; + while (idx[i]index_entries[idx[i]].pos < pos) + idx[i]++; + if (idx[i] < n) { + min_dts = FFMIN(min_dts, av_rescale_q(st->index_entries[idx[i]].timestamp, st->time_base, AV_TIME_BASE_Q)); + min_pos = FFMIN(min_pos, st->index_entries[idx[i]].pos); + } + if (idx[i]) + max_dts = FFMAX(max_dts, av_rescale_q(st->index_entries[idx[i]-1].timestamp, st->time_base, AV_TIME_BASE_Q)); + } + if(max_dts - min_dts > 2*AV_TIME_BASE) { + av_free(idx); + return 1; + } + } + av_free(idx); + return 0; +} + +static int avi_load_index(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t tag, size; + int64_t pos= avio_tell(pb); + int64_t next; + int ret = -1; + + if (avio_seek(pb, avi->movi_end, SEEK_SET) < 0) + goto the_end; // maybe truncated file + av_dlog(s, "movi_end=0x%"PRIx64"\n", avi->movi_end); + for(;;) { + tag = avio_rl32(pb); + size = avio_rl32(pb); + if (url_feof(pb)) + break; + next = avio_tell(pb) + size + (size & 1); + + av_dlog(s, "tag=%c%c%c%c size=0x%x\n", + tag & 0xff, + (tag >> 8) & 0xff, + (tag >> 16) & 0xff, + (tag >> 24) & 0xff, + size); + + if (tag == MKTAG('i', 'd', 'x', '1') && + avi_read_idx1(s, size) >= 0) { + avi->index_loaded=2; + ret = 0; + }else if(tag == MKTAG('L', 'I', 'S', 'T')) { + uint32_t tag1 = avio_rl32(pb); + + if (tag1 == MKTAG('I', 'N', 'F', 'O')) + ff_read_riff_info(s, size - 4); + }else if(!ret) + break; + + if (avio_seek(pb, next, SEEK_SET) < 0) + break; // something is wrong here + } + the_end: + avio_seek(pb, pos, SEEK_SET); + return ret; +} + +static void seek_subtitle(AVStream *st, AVStream *st2, int64_t timestamp) +{ + AVIStream *ast2 = st2->priv_data; + int64_t ts2 = av_rescale_q(timestamp, st->time_base, st2->time_base); + av_free_packet(&ast2->sub_pkt); + if (avformat_seek_file(ast2->sub_ctx, 0, INT64_MIN, ts2, ts2, 0) >= 0 || + avformat_seek_file(ast2->sub_ctx, 0, ts2, ts2, INT64_MAX, 0) >= 0) + ff_read_packet(ast2->sub_ctx, &ast2->sub_pkt); +} + +static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVIContext *avi = s->priv_data; + AVStream *st; + int i, index; + int64_t pos, pos_min; + AVIStream *ast; + + if (!avi->index_loaded) { + /* we only load the index on demand */ + avi_load_index(s); + avi->index_loaded |= 1; + } + av_assert0(stream_index>= 0); + + st = s->streams[stream_index]; + ast= st->priv_data; + index= av_index_search_timestamp(st, timestamp * FFMAX(ast->sample_size, 1), flags); + if (index<0) { + if (st->nb_index_entries > 0) + av_log(s, AV_LOG_DEBUG, "Failed to find timestamp %"PRId64 " in index %"PRId64 " .. %"PRId64 "\n", + timestamp * FFMAX(ast->sample_size, 1), + st->index_entries[0].timestamp, + st->index_entries[st->nb_index_entries - 1].timestamp); + return AVERROR_INVALIDDATA; + } + + /* find the position */ + pos = st->index_entries[index].pos; + timestamp = st->index_entries[index].timestamp / FFMAX(ast->sample_size, 1); + + av_dlog(s, "XX %"PRId64" %d %"PRId64"\n", + timestamp, index, st->index_entries[index].timestamp); + + if (CONFIG_DV_DEMUXER && avi->dv_demux) { + /* One and only one real stream for DV in AVI, and it has video */ + /* offsets. Calling with other stream indexes should have failed */ + /* the av_index_search_timestamp call above. */ + av_assert0(stream_index == 0); + + if(avio_seek(s->pb, pos, SEEK_SET) < 0) + return -1; + + /* Feed the DV video stream version of the timestamp to the */ + /* DV demux so it can synthesize correct timestamps. */ + ff_dv_offset_reset(avi->dv_demux, timestamp); + + avi->stream_index= -1; + return 0; + } + + pos_min= pos; + for(i = 0; i < s->nb_streams; i++) { + AVStream *st2 = s->streams[i]; + AVIStream *ast2 = st2->priv_data; + + ast2->packet_size= + ast2->remaining= 0; + + if (ast2->sub_ctx) { + seek_subtitle(st, st2, timestamp); + continue; + } + + if (st2->nb_index_entries <= 0) + continue; + +// av_assert1(st2->codec->block_align); + av_assert0((int64_t)st2->time_base.num*ast2->rate == (int64_t)st2->time_base.den*ast2->scale); + index = av_index_search_timestamp( + st2, + av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), + flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); + if(index<0) + index=0; + ast2->seek_pos= st2->index_entries[index].pos; + pos_min= FFMIN(pos_min,ast2->seek_pos); + } + for(i = 0; i < s->nb_streams; i++) { + AVStream *st2 = s->streams[i]; + AVIStream *ast2 = st2->priv_data; + + if (ast2->sub_ctx || st2->nb_index_entries <= 0) + continue; + + index = av_index_search_timestamp( + st2, + av_rescale_q(timestamp, st->time_base, st2->time_base) * FFMAX(ast2->sample_size, 1), + flags | AVSEEK_FLAG_BACKWARD | (st2->codec->codec_type != AVMEDIA_TYPE_VIDEO ? AVSEEK_FLAG_ANY : 0)); + if(index<0) + index=0; + while(!avi->non_interleaved && index>0 && st2->index_entries[index-1].pos >= pos_min) + index--; + ast2->frame_offset = st2->index_entries[index].timestamp; + } + + /* do the seek */ + if (avio_seek(s->pb, pos_min, SEEK_SET) < 0) { + av_log(s, AV_LOG_ERROR, "Seek failed\n"); + return -1; + } + avi->stream_index= -1; + avi->dts_max= INT_MIN; + return 0; +} + +static int avi_read_close(AVFormatContext *s) +{ + int i; + AVIContext *avi = s->priv_data; + + for(i=0;inb_streams;i++) { + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + if (ast) { + if (ast->sub_ctx) { + av_freep(&ast->sub_ctx->pb); + avformat_close_input(&ast->sub_ctx); + } + av_free(ast->sub_buffer); + av_free_packet(&ast->sub_pkt); + } + } + + av_free(avi->dv_demux); + + return 0; +} + +static int avi_probe(AVProbeData *p) +{ + int i; + + /* check file header */ + for(i=0; avi_headers[i][0]; i++) + if(!memcmp(p->buf , avi_headers[i] , 4) && + !memcmp(p->buf+8, avi_headers[i]+4, 4)) + return AVPROBE_SCORE_MAX; + + return 0; +} + +AVInputFormat ff_avi_demuxer = { + .name = "avi", + .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), + .priv_data_size = sizeof(AVIContext), + .read_probe = avi_probe, + .read_header = avi_read_header, + .read_packet = avi_read_packet, + .read_close = avi_read_close, + .read_seek = avi_read_seek, + .priv_class = &demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avidec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avidec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/avidec.o libavformat/avidec.o: libavformat/avidec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/bswap.h libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavutil/dict.h libavutil/avstring.h \ + libavutil/avassert.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avi.h libavformat/dv.h libavformat/riff.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avidec.o Binary file ffmpeg/libavformat/avidec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avienc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avienc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,662 @@ +/* + * AVI muxer + * Copyright (c) 2000 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//#define DEBUG + +#include "avformat.h" +#include "internal.h" +#include "avi.h" +#include "avio_internal.h" +#include "riff.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "libavutil/avassert.h" +#include "libavutil/timestamp.h" + +/* + * TODO: + * - fill all fields if non streamed (nb_frames for example) + */ + +typedef struct AVIIentry { + unsigned int flags, pos, len; +} AVIIentry; + +#define AVI_INDEX_CLUSTER_SIZE 16384 + +typedef struct AVIIndex { + int64_t indx_start; + int entry; + int ents_allocated; + AVIIentry** cluster; +} AVIIndex; + +typedef struct { + int64_t riff_start, movi_list, odml_list; + int64_t frames_hdr_all; + int riff_id; +} AVIContext; + +typedef struct { + int64_t frames_hdr_strm; + int64_t audio_strm_length; + int packet_count; + int entry; + + AVIIndex indexes; +} AVIStream ; + +static inline AVIIentry* avi_get_ientry(AVIIndex* idx, int ent_id) +{ + int cl = ent_id / AVI_INDEX_CLUSTER_SIZE; + int id = ent_id % AVI_INDEX_CLUSTER_SIZE; + return &idx->cluster[cl][id]; +} + +static int64_t avi_start_new_riff(AVFormatContext *s, AVIOContext *pb, + const char* riff_tag, const char* list_tag) +{ + AVIContext *avi= s->priv_data; + int64_t loff; + int i; + + avi->riff_id++; + for (i=0; inb_streams; i++){ + AVIStream *avist= s->streams[i]->priv_data; + avist->indexes.entry = 0; + } + + avi->riff_start = ff_start_tag(pb, "RIFF"); + ffio_wfourcc(pb, riff_tag); + loff = ff_start_tag(pb, "LIST"); + ffio_wfourcc(pb, list_tag); + return loff; +} + +static char* avi_stream2fourcc(char* tag, int index, enum AVMediaType type) +{ + tag[0] = '0' + index/10; + tag[1] = '0' + index%10; + if (type == AVMEDIA_TYPE_VIDEO) { + tag[2] = 'd'; + tag[3] = 'c'; + } else if (type == AVMEDIA_TYPE_SUBTITLE) { + // note: this is not an official code + tag[2] = 's'; + tag[3] = 'b'; + } else { + tag[2] = 'w'; + tag[3] = 'b'; + } + tag[4] = '\0'; + return tag; +} + +static int avi_write_counters(AVFormatContext* s, int riff_id) +{ + AVIOContext *pb = s->pb; + AVIContext *avi = s->priv_data; + int n, au_byterate, au_ssize, au_scale, nb_frames = 0; + int64_t file_size; + AVCodecContext* stream; + + file_size = avio_tell(pb); + for(n = 0; n < s->nb_streams; n++) { + AVIStream *avist= s->streams[n]->priv_data; + + av_assert0(avist->frames_hdr_strm); + stream = s->streams[n]->codec; + avio_seek(pb, avist->frames_hdr_strm, SEEK_SET); + ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); + if(au_ssize == 0) { + avio_wl32(pb, avist->packet_count); + } else { + avio_wl32(pb, avist->audio_strm_length / au_ssize); + } + if(stream->codec_type == AVMEDIA_TYPE_VIDEO) + nb_frames = FFMAX(nb_frames, avist->packet_count); + } + if(riff_id == 1) { + av_assert0(avi->frames_hdr_all); + avio_seek(pb, avi->frames_hdr_all, SEEK_SET); + avio_wl32(pb, nb_frames); + } + avio_seek(pb, file_size, SEEK_SET); + + return 0; +} + +static int avi_write_header(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale; + AVCodecContext *stream, *video_enc; + int64_t list1, list2, strh, strf; + AVDictionaryEntry *t = NULL; + + if (s->nb_streams > AVI_MAX_STREAM_COUNT) { + av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n", + AVI_MAX_STREAM_COUNT); + return AVERROR(EINVAL); + } + + for(n=0;nnb_streams;n++) { + s->streams[n]->priv_data= av_mallocz(sizeof(AVIStream)); + if(!s->streams[n]->priv_data) + return AVERROR(ENOMEM); + } + + /* header list */ + avi->riff_id = 0; + list1 = avi_start_new_riff(s, pb, "AVI ", "hdrl"); + + /* avi header */ + ffio_wfourcc(pb, "avih"); + avio_wl32(pb, 14 * 4); + bitrate = 0; + + video_enc = NULL; + for(n=0;nnb_streams;n++) { + stream = s->streams[n]->codec; + bitrate += stream->bit_rate; + if (stream->codec_type == AVMEDIA_TYPE_VIDEO) + video_enc = stream; + } + + nb_frames = 0; + + if(video_enc){ + avio_wl32(pb, (uint32_t)(INT64_C(1000000) * video_enc->time_base.num / video_enc->time_base.den)); + } else { + avio_wl32(pb, 0); + } + avio_wl32(pb, bitrate / 8); /* XXX: not quite exact */ + avio_wl32(pb, 0); /* padding */ + if (!pb->seekable) + avio_wl32(pb, AVIF_TRUSTCKTYPE | AVIF_ISINTERLEAVED); /* flags */ + else + avio_wl32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */ + avi->frames_hdr_all = avio_tell(pb); /* remember this offset to fill later */ + avio_wl32(pb, nb_frames); /* nb frames, filled later */ + avio_wl32(pb, 0); /* initial frame */ + avio_wl32(pb, s->nb_streams); /* nb streams */ + avio_wl32(pb, 1024 * 1024); /* suggested buffer size */ + if(video_enc){ + avio_wl32(pb, video_enc->width); + avio_wl32(pb, video_enc->height); + } else { + avio_wl32(pb, 0); + avio_wl32(pb, 0); + } + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + + /* stream list */ + for(i=0;istreams[i]->priv_data; + list2 = ff_start_tag(pb, "LIST"); + ffio_wfourcc(pb, "strl"); + + stream = s->streams[i]->codec; + + /* stream generic header */ + strh = ff_start_tag(pb, "strh"); + switch(stream->codec_type) { + case AVMEDIA_TYPE_SUBTITLE: + // XSUB subtitles behave like video tracks, other subtitles + // are not (yet) supported. + if (stream->codec_id != AV_CODEC_ID_XSUB) { + av_log(s, AV_LOG_ERROR, "Subtitle streams other than DivX XSUB are not supported by the AVI muxer.\n"); + return AVERROR_PATCHWELCOME; + } + case AVMEDIA_TYPE_VIDEO: ffio_wfourcc(pb, "vids"); break; + case AVMEDIA_TYPE_AUDIO: ffio_wfourcc(pb, "auds"); break; +// case AVMEDIA_TYPE_TEXT : ffio_wfourcc(pb, "txts"); break; + case AVMEDIA_TYPE_DATA : ffio_wfourcc(pb, "dats"); break; + } + if(stream->codec_type == AVMEDIA_TYPE_VIDEO || + stream->codec_id == AV_CODEC_ID_XSUB) + avio_wl32(pb, stream->codec_tag); + else + avio_wl32(pb, 1); + avio_wl32(pb, 0); /* flags */ + avio_wl16(pb, 0); /* priority */ + avio_wl16(pb, 0); /* language */ + avio_wl32(pb, 0); /* initial frame */ + + ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale); + + if ( stream->codec_type == AVMEDIA_TYPE_VIDEO + && stream->codec_id != AV_CODEC_ID_XSUB + && au_byterate > 1000LL*au_scale) { + au_byterate = 600; + au_scale = 1; + } + avpriv_set_pts_info(s->streams[i], 64, au_scale, au_byterate); + if(stream->codec_id == AV_CODEC_ID_XSUB) + au_scale = au_byterate = 0; + + avio_wl32(pb, au_scale); /* scale */ + avio_wl32(pb, au_byterate); /* rate */ + + avio_wl32(pb, 0); /* start */ + avist->frames_hdr_strm = avio_tell(pb); /* remember this offset to fill later */ + if (!pb->seekable) + avio_wl32(pb, AVI_MAX_RIFF_SIZE); /* FIXME: this may be broken, but who cares */ + else + avio_wl32(pb, 0); /* length, XXX: filled later */ + + /* suggested buffer size */ //FIXME set at the end to largest chunk + if(stream->codec_type == AVMEDIA_TYPE_VIDEO) + avio_wl32(pb, 1024 * 1024); + else if(stream->codec_type == AVMEDIA_TYPE_AUDIO) + avio_wl32(pb, 12 * 1024); + else + avio_wl32(pb, 0); + avio_wl32(pb, -1); /* quality */ + avio_wl32(pb, au_ssize); /* sample size */ + avio_wl32(pb, 0); + avio_wl16(pb, stream->width); + avio_wl16(pb, stream->height); + ff_end_tag(pb, strh); + + if(stream->codec_type != AVMEDIA_TYPE_DATA){ + int ret; + + strf = ff_start_tag(pb, "strf"); + switch(stream->codec_type) { + case AVMEDIA_TYPE_SUBTITLE: + // XSUB subtitles behave like video tracks, other subtitles + // are not (yet) supported. + if (stream->codec_id != AV_CODEC_ID_XSUB) break; + case AVMEDIA_TYPE_VIDEO: + ff_put_bmp_header(pb, stream, ff_codec_bmp_tags, 0); + break; + case AVMEDIA_TYPE_AUDIO: + if ((ret = ff_put_wav_header(pb, stream)) < 0) { + return ret; + } + break; + default: + av_log(s, AV_LOG_ERROR, + "Invalid or not supported codec type '%s' found in the input\n", + (char *)av_x_if_null(av_get_media_type_string(stream->codec_type), "?")); + return AVERROR(EINVAL); + } + ff_end_tag(pb, strf); + if ((t = av_dict_get(s->streams[i]->metadata, "title", NULL, 0))) { + ff_riff_write_info_tag(s->pb, "strn", t->value); + t = NULL; + } + } + + if (pb->seekable) { + unsigned char tag[5]; + int j; + + /* Starting to lay out AVI OpenDML master index. + * We want to make it JUNK entry for now, since we'd + * like to get away without making AVI an OpenDML one + * for compatibility reasons. + */ + avist->indexes.entry = avist->indexes.ents_allocated = 0; + avist->indexes.indx_start = ff_start_tag(pb, "JUNK"); + avio_wl16(pb, 4); /* wLongsPerEntry */ + avio_w8(pb, 0); /* bIndexSubType (0 == frame index) */ + avio_w8(pb, 0); /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */ + avio_wl32(pb, 0); /* nEntriesInUse (will fill out later on) */ + ffio_wfourcc(pb, avi_stream2fourcc(tag, i, stream->codec_type)); + /* dwChunkId */ + avio_wl64(pb, 0); /* dwReserved[3] + avio_wl32(pb, 0); Must be 0. */ + for (j=0; j < AVI_MASTER_INDEX_SIZE * 2; j++) + avio_wl64(pb, 0); + ff_end_tag(pb, avist->indexes.indx_start); + } + + if( stream->codec_type == AVMEDIA_TYPE_VIDEO + && s->streams[i]->sample_aspect_ratio.num>0 + && s->streams[i]->sample_aspect_ratio.den>0){ + int vprp= ff_start_tag(pb, "vprp"); + AVRational dar = av_mul_q(s->streams[i]->sample_aspect_ratio, + (AVRational){stream->width, stream->height}); + int num, den; + av_reduce(&num, &den, dar.num, dar.den, 0xFFFF); + + avio_wl32(pb, 0); //video format = unknown + avio_wl32(pb, 0); //video standard= unknown + avio_wl32(pb, lrintf(1.0/av_q2d(stream->time_base))); + avio_wl32(pb, stream->width ); + avio_wl32(pb, stream->height); + avio_wl16(pb, den); + avio_wl16(pb, num); + avio_wl32(pb, stream->width ); + avio_wl32(pb, stream->height); + avio_wl32(pb, 1); //progressive FIXME + + avio_wl32(pb, stream->height); + avio_wl32(pb, stream->width ); + avio_wl32(pb, stream->height); + avio_wl32(pb, stream->width ); + avio_wl32(pb, 0); + avio_wl32(pb, 0); + + avio_wl32(pb, 0); + avio_wl32(pb, 0); + ff_end_tag(pb, vprp); + } + + ff_end_tag(pb, list2); + } + + if (pb->seekable) { + /* AVI could become an OpenDML one, if it grows beyond 2Gb range */ + avi->odml_list = ff_start_tag(pb, "JUNK"); + ffio_wfourcc(pb, "odml"); + ffio_wfourcc(pb, "dmlh"); + avio_wl32(pb, 248); + for (i = 0; i < 248; i+= 4) + avio_wl32(pb, 0); + ff_end_tag(pb, avi->odml_list); + } + + ff_end_tag(pb, list1); + + ff_riff_write_info(s); + + /* some padding for easier tag editing */ + list2 = ff_start_tag(pb, "JUNK"); + for (i = 0; i < 1016; i += 4) + avio_wl32(pb, 0); + ff_end_tag(pb, list2); + + avi->movi_list = ff_start_tag(pb, "LIST"); + ffio_wfourcc(pb, "movi"); + + avio_flush(pb); + + return 0; +} + +static int avi_write_ix(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVIContext *avi = s->priv_data; + char tag[5]; + char ix_tag[] = "ix00"; + int i, j; + + av_assert0(pb->seekable); + + if (avi->riff_id > AVI_MASTER_INDEX_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid riff index %d > %d\n", + avi->riff_id, AVI_MASTER_INDEX_SIZE); + return AVERROR(EINVAL); + } + + for (i=0;inb_streams;i++) { + AVIStream *avist= s->streams[i]->priv_data; + int64_t ix, pos; + + avi_stream2fourcc(tag, i, s->streams[i]->codec->codec_type); + ix_tag[3] = '0' + i; + + /* Writing AVI OpenDML leaf index chunk */ + ix = avio_tell(pb); + ffio_wfourcc(pb, ix_tag); /* ix?? */ + avio_wl32(pb, avist->indexes.entry * 8 + 24); + /* chunk size */ + avio_wl16(pb, 2); /* wLongsPerEntry */ + avio_w8(pb, 0); /* bIndexSubType (0 == frame index) */ + avio_w8(pb, 1); /* bIndexType (1 == AVI_INDEX_OF_CHUNKS) */ + avio_wl32(pb, avist->indexes.entry); + /* nEntriesInUse */ + ffio_wfourcc(pb, tag); /* dwChunkId */ + avio_wl64(pb, avi->movi_list);/* qwBaseOffset */ + avio_wl32(pb, 0); /* dwReserved_3 (must be 0) */ + + for (j=0; jindexes.entry; j++) { + AVIIentry* ie = avi_get_ientry(&avist->indexes, j); + avio_wl32(pb, ie->pos + 8); + avio_wl32(pb, ((uint32_t)ie->len & ~0x80000000) | + (ie->flags & 0x10 ? 0 : 0x80000000)); + } + avio_flush(pb); + pos = avio_tell(pb); + + /* Updating one entry in the AVI OpenDML master index */ + avio_seek(pb, avist->indexes.indx_start - 8, SEEK_SET); + ffio_wfourcc(pb, "indx"); /* enabling this entry */ + avio_skip(pb, 8); + avio_wl32(pb, avi->riff_id); /* nEntriesInUse */ + avio_skip(pb, 16*avi->riff_id); + avio_wl64(pb, ix); /* qwOffset */ + avio_wl32(pb, pos - ix); /* dwSize */ + avio_wl32(pb, avist->indexes.entry); /* dwDuration */ + + avio_seek(pb, pos, SEEK_SET); + } + return 0; +} + +static int avi_write_idx1(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVIContext *avi = s->priv_data; + int64_t idx_chunk; + int i; + char tag[5]; + + if (pb->seekable) { + AVIStream *avist; + AVIIentry* ie = 0, *tie; + int empty, stream_id = -1; + + idx_chunk = ff_start_tag(pb, "idx1"); + for(i=0; inb_streams; i++){ + avist= s->streams[i]->priv_data; + avist->entry=0; + } + + do { + empty = 1; + for (i=0; inb_streams; i++) { + avist= s->streams[i]->priv_data; + if (avist->indexes.entry <= avist->entry) + continue; + + tie = avi_get_ientry(&avist->indexes, avist->entry); + if (empty || tie->pos < ie->pos) { + ie = tie; + stream_id = i; + } + empty = 0; + } + if (!empty) { + avist= s->streams[stream_id]->priv_data; + avi_stream2fourcc(tag, stream_id, + s->streams[stream_id]->codec->codec_type); + ffio_wfourcc(pb, tag); + avio_wl32(pb, ie->flags); + avio_wl32(pb, ie->pos); + avio_wl32(pb, ie->len); + avist->entry++; + } + } while (!empty); + ff_end_tag(pb, idx_chunk); + + avi_write_counters(s, avi->riff_id); + } + return 0; +} + +static int avi_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char tag[5]; + unsigned int flags=0; + const int stream_index= pkt->stream_index; + AVIStream *avist= s->streams[stream_index]->priv_data; + AVCodecContext *enc= s->streams[stream_index]->codec; + int size= pkt->size; + + av_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(pkt->dts), avist->packet_count, stream_index); + while(enc->block_align==0 && pkt->dts != AV_NOPTS_VALUE && pkt->dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB){ + AVPacket empty_packet; + + if(pkt->dts - avist->packet_count > 60000){ + av_log(s, AV_LOG_ERROR, "Too large number of skipped frames %"PRId64" > 60000\n", pkt->dts - avist->packet_count); + return AVERROR(EINVAL); + } + + av_init_packet(&empty_packet); + empty_packet.size= 0; + empty_packet.data= NULL; + empty_packet.stream_index= stream_index; + avi_write_packet(s, &empty_packet); + av_dlog(s, "dup dts:%s packet_count:%d\n", av_ts2str(pkt->dts), avist->packet_count); + } + avist->packet_count++; + + // Make sure to put an OpenDML chunk when the file size exceeds the limits + if (pb->seekable && + (avio_tell(pb) - avi->riff_start > AVI_MAX_RIFF_SIZE)) { + + avi_write_ix(s); + ff_end_tag(pb, avi->movi_list); + + if (avi->riff_id == 1) + avi_write_idx1(s); + + ff_end_tag(pb, avi->riff_start); + avi->movi_list = avi_start_new_riff(s, pb, "AVIX", "movi"); + } + + avi_stream2fourcc(tag, stream_index, enc->codec_type); + if(pkt->flags&AV_PKT_FLAG_KEY) + flags = 0x10; + if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { + avist->audio_strm_length += size; + } + + if (s->pb->seekable) { + AVIIndex* idx = &avist->indexes; + int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE; + int id = idx->entry % AVI_INDEX_CLUSTER_SIZE; + if (idx->ents_allocated <= idx->entry) { + idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1); + if (!idx->cluster) + return AVERROR(ENOMEM); + idx->cluster[cl] = av_malloc(AVI_INDEX_CLUSTER_SIZE*sizeof(AVIIentry)); + if (!idx->cluster[cl]) + return AVERROR(ENOMEM); + idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE; + } + + idx->cluster[cl][id].flags = flags; + idx->cluster[cl][id].pos = avio_tell(pb) - avi->movi_list; + idx->cluster[cl][id].len = size; + idx->entry++; + } + + avio_write(pb, tag, 4); + avio_wl32(pb, size); + avio_write(pb, pkt->data, size); + if (size & 1) + avio_w8(pb, 0); + + return 0; +} + +static int avi_write_trailer(AVFormatContext *s) +{ + AVIContext *avi = s->priv_data; + AVIOContext *pb = s->pb; + int res = 0; + int i, j, n, nb_frames; + int64_t file_size; + + if (pb->seekable){ + if (avi->riff_id == 1) { + ff_end_tag(pb, avi->movi_list); + res = avi_write_idx1(s); + ff_end_tag(pb, avi->riff_start); + } else { + avi_write_ix(s); + ff_end_tag(pb, avi->movi_list); + ff_end_tag(pb, avi->riff_start); + + file_size = avio_tell(pb); + avio_seek(pb, avi->odml_list - 8, SEEK_SET); + ffio_wfourcc(pb, "LIST"); /* Making this AVI OpenDML one */ + avio_skip(pb, 16); + + for (n=nb_frames=0;nnb_streams;n++) { + AVCodecContext *stream = s->streams[n]->codec; + AVIStream *avist= s->streams[n]->priv_data; + + if (stream->codec_type == AVMEDIA_TYPE_VIDEO) { + if (nb_frames < avist->packet_count) + nb_frames = avist->packet_count; + } else { + if (stream->codec_id == AV_CODEC_ID_MP2 || stream->codec_id == AV_CODEC_ID_MP3) { + nb_frames += avist->packet_count; + } + } + } + avio_wl32(pb, nb_frames); + avio_seek(pb, file_size, SEEK_SET); + + avi_write_counters(s, avi->riff_id); + } + } + + for (i=0; inb_streams; i++) { + AVIStream *avist= s->streams[i]->priv_data; + for (j=0; jindexes.ents_allocated/AVI_INDEX_CLUSTER_SIZE; j++) + av_free(avist->indexes.cluster[j]); + av_freep(&avist->indexes.cluster); + avist->indexes.ents_allocated = avist->indexes.entry = 0; + } + + return res; +} + +AVOutputFormat ff_avi_muxer = { + .name = "avi", + .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), + .mime_type = "video/x-msvideo", + .extensions = "avi", + .priv_data_size = sizeof(AVIContext), + .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3, + .video_codec = AV_CODEC_ID_MPEG4, + .write_header = avi_write_header, + .write_packet = avi_write_packet, + .write_trailer = avi_write_trailer, + .codec_tag = (const AVCodecTag* const []){ + ff_codec_bmp_tags, ff_codec_wav_tags, 0 + }, + .flags = AVFMT_VARIABLE_FPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avienc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avienc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/avienc.o libavformat/avienc.o: libavformat/avienc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avi.h libavformat/avio_internal.h libavformat/url.h \ + libavformat/riff.h libavformat/metadata.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/avassert.h libavutil/timestamp.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avienc.o Binary file ffmpeg/libavformat/avienc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avio.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,434 @@ +/* + * unbuffered I/O + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "os_support.h" +#include "avformat.h" +#if CONFIG_NETWORK +#include "network.h" +#endif +#include "url.h" + +static URLProtocol *first_protocol = NULL; + +URLProtocol *ffurl_protocol_next(URLProtocol *prev) +{ + return prev ? prev->next : first_protocol; +} + +/** @name Logging context. */ +/*@{*/ +static const char *urlcontext_to_name(void *ptr) +{ + URLContext *h = (URLContext *)ptr; + if(h->prot) return h->prot->name; + else return "NULL"; +} + +static void *urlcontext_child_next(void *obj, void *prev) +{ + URLContext *h = obj; + if (!prev && h->priv_data && h->prot->priv_data_class) + return h->priv_data; + return NULL; +} + +static const AVClass *urlcontext_child_class_next(const AVClass *prev) +{ + URLProtocol *p = NULL; + + /* find the protocol that corresponds to prev */ + while (prev && (p = ffurl_protocol_next(p))) + if (p->priv_data_class == prev) + break; + + /* find next protocol with priv options */ + while (p = ffurl_protocol_next(p)) + if (p->priv_data_class) + return p->priv_data_class; + return NULL; + +} + +static const AVOption options[] = {{NULL}}; +const AVClass ffurl_context_class = { + .class_name = "URLContext", + .item_name = urlcontext_to_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .child_next = urlcontext_child_next, + .child_class_next = urlcontext_child_class_next, +}; +/*@}*/ + + +const char *avio_enum_protocols(void **opaque, int output) +{ + URLProtocol *p; + *opaque = ffurl_protocol_next(*opaque); + if (!(p = *opaque)) return NULL; + if ((output && p->url_write) || (!output && p->url_read)) + return p->name; + return avio_enum_protocols(opaque, output); +} + +int ffurl_register_protocol(URLProtocol *protocol, int size) +{ + URLProtocol **p; + if (size < sizeof(URLProtocol)) { + URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); + memcpy(temp, protocol, size); + protocol = temp; + } + p = &first_protocol; + while (*p != NULL) p = &(*p)->next; + *p = protocol; + protocol->next = NULL; + return 0; +} + +static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, + const char *filename, int flags, + const AVIOInterruptCB *int_cb) +{ + URLContext *uc; + int err; + +#if CONFIG_NETWORK + if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init()) + return AVERROR(EIO); +#endif + if ((flags & AVIO_FLAG_READ) && !up->url_read) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to open the '%s' protocol for reading\n", up->name); + return AVERROR(EIO); + } + if ((flags & AVIO_FLAG_WRITE) && !up->url_write) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to open the '%s' protocol for writing\n", up->name); + return AVERROR(EIO); + } + uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); + if (!uc) { + err = AVERROR(ENOMEM); + goto fail; + } + uc->av_class = &ffurl_context_class; + uc->filename = (char *) &uc[1]; + strcpy(uc->filename, filename); + uc->prot = up; + uc->flags = flags; + uc->is_streamed = 0; /* default = not streamed */ + uc->max_packet_size = 0; /* default: stream file */ + if (up->priv_data_size) { + uc->priv_data = av_mallocz(up->priv_data_size); + if (up->priv_data_class) { + int proto_len= strlen(up->name); + char *start = strchr(uc->filename, ','); + *(const AVClass**)uc->priv_data = up->priv_data_class; + av_opt_set_defaults(uc->priv_data); + if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){ + int ret= 0; + char *p= start; + char sep= *++p; + char *key, *val; + p++; + while(ret >= 0 && (key= strchr(p, sep)) && ppriv_data, p, key+1, 0); + if (ret == AVERROR_OPTION_NOT_FOUND) + av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); + *val= *key= sep; + p= val+1; + } + if(ret<0 || p!=key){ + av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); + av_freep(&uc->priv_data); + av_freep(&uc); + err = AVERROR(EINVAL); + goto fail; + } + memmove(start, key+1, strlen(key)); + } + } + } + if (int_cb) + uc->interrupt_callback = *int_cb; + + *puc = uc; + return 0; + fail: + *puc = NULL; +#if CONFIG_NETWORK + if (up->flags & URL_PROTOCOL_FLAG_NETWORK) + ff_network_close(); +#endif + return err; +} + +int ffurl_connect(URLContext* uc, AVDictionary **options) +{ + int err = + uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) : + uc->prot->url_open(uc, uc->filename, uc->flags); + if (err) + return err; + uc->is_connected = 1; + //We must be careful here as ffurl_seek() could be slow, for example for http + if( (uc->flags & AVIO_FLAG_WRITE) + || !strcmp(uc->prot->name, "file")) + if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0) + uc->is_streamed= 1; + return 0; +} + +#define URL_SCHEME_CHARS \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789+-." + +int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb) +{ + URLProtocol *up = NULL; + char proto_str[128], proto_nested[128], *ptr; + size_t proto_len = strspn(filename, URL_SCHEME_CHARS); + + if (!first_protocol) { + av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. " + "Missing call to av_register_all()?\n"); + } + + if (filename[proto_len] != ':' && + (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) || + is_dos_path(filename)) + strcpy(proto_str, "file"); + else + av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str))); + + if ((ptr = strchr(proto_str, ','))) + *ptr = '\0'; + av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); + if ((ptr = strchr(proto_nested, '+'))) + *ptr = '\0'; + + while (up = ffurl_protocol_next(up)) { + if (!strcmp(proto_str, up->name)) + return url_alloc_for_protocol (puc, up, filename, flags, int_cb); + if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && + !strcmp(proto_nested, up->name)) + return url_alloc_for_protocol (puc, up, filename, flags, int_cb); + } + *puc = NULL; + return AVERROR_PROTOCOL_NOT_FOUND; +} + +int ffurl_open(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + int ret = ffurl_alloc(puc, filename, flags, int_cb); + if (ret) + return ret; + if (options && (*puc)->prot->priv_data_class && + (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) + goto fail; + ret = ffurl_connect(*puc, options); + if (!ret) + return 0; +fail: + ffurl_close(*puc); + *puc = NULL; + return ret; +} + +static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min, + int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) +{ + int ret, len; + int fast_retries = 5; + int64_t wait_since = 0; + + len = 0; + while (len < size_min) { + ret = transfer_func(h, buf+len, size-len); + if (ret == AVERROR(EINTR)) + continue; + if (h->flags & AVIO_FLAG_NONBLOCK) + return ret; + if (ret == AVERROR(EAGAIN)) { + ret = 0; + if (fast_retries) { + fast_retries--; + } else { + if (h->rw_timeout) { + if (!wait_since) + wait_since = av_gettime(); + else if (av_gettime() > wait_since + h->rw_timeout) + return AVERROR(EIO); + } + av_usleep(1000); + } + } else if (ret < 1) + return ret < 0 ? ret : len; + if (ret) + fast_retries = FFMAX(fast_retries, 2); + len += ret; + if (len < size && ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; + } + return len; +} + +int ffurl_read(URLContext *h, unsigned char *buf, int size) +{ + if (!(h->flags & AVIO_FLAG_READ)) + return AVERROR(EIO); + return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); +} + +int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) +{ + if (!(h->flags & AVIO_FLAG_READ)) + return AVERROR(EIO); + return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read); +} + +int ffurl_write(URLContext *h, const unsigned char *buf, int size) +{ + if (!(h->flags & AVIO_FLAG_WRITE)) + return AVERROR(EIO); + /* avoid sending too big packets */ + if (h->max_packet_size && size > h->max_packet_size) + return AVERROR(EIO); + + return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write); +} + +int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) +{ + int64_t ret; + + if (!h->prot->url_seek) + return AVERROR(ENOSYS); + ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); + return ret; +} + +int ffurl_closep(URLContext **hh) +{ + URLContext *h= *hh; + int ret = 0; + if (!h) return 0; /* can happen when ffurl_open fails */ + + if (h->is_connected && h->prot->url_close) + ret = h->prot->url_close(h); +#if CONFIG_NETWORK + if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK) + ff_network_close(); +#endif + if (h->prot->priv_data_size) { + if (h->prot->priv_data_class) + av_opt_free(h->priv_data); + av_freep(&h->priv_data); + } + av_freep(hh); + return ret; +} + +int ffurl_close(URLContext *h) +{ + return ffurl_closep(&h); +} + + +int avio_check(const char *url, int flags) +{ + URLContext *h; + int ret = ffurl_alloc(&h, url, flags, NULL); + if (ret) + return ret; + + if (h->prot->url_check) { + ret = h->prot->url_check(h, flags); + } else { + ret = ffurl_connect(h, NULL); + if (ret >= 0) + ret = flags; + } + + ffurl_close(h); + return ret; +} + +int64_t ffurl_size(URLContext *h) +{ + int64_t pos, size; + + size= ffurl_seek(h, 0, AVSEEK_SIZE); + if(size<0){ + pos = ffurl_seek(h, 0, SEEK_CUR); + if ((size = ffurl_seek(h, -1, SEEK_END)) < 0) + return size; + size++; + ffurl_seek(h, pos, SEEK_SET); + } + return size; +} + +int ffurl_get_file_handle(URLContext *h) +{ + if (!h->prot->url_get_file_handle) + return -1; + return h->prot->url_get_file_handle(h); +} + +int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) +{ + if (!h->prot->url_get_multi_file_handle) { + if (!h->prot->url_get_file_handle) + return AVERROR(ENOSYS); + *handles = av_malloc(sizeof(**handles)); + if (!*handles) + return AVERROR(ENOMEM); + *numhandles = 1; + *handles[0] = h->prot->url_get_file_handle(h); + return 0; + } + return h->prot->url_get_multi_file_handle(h, handles, numhandles); +} + +int ffurl_shutdown(URLContext *h, int flags) +{ + if (!h->prot->url_shutdown) + return AVERROR(EINVAL); + return h->prot->url_shutdown(h, flags); +} + +int ff_check_interrupt(AVIOInterruptCB *cb) +{ + int ret; + if (cb && cb->callback && (ret = cb->callback(cb->opaque))) + return ret; + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avio.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avio.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/avio.o libavformat/avio.o: libavformat/avio.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/dict.h \ + libavutil/opt.h libavutil/rational.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/time.h libavformat/os_support.h config.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/network.h libavutil/error.h \ + libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avio.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,481 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVFORMAT_AVIO_H +#define AVFORMAT_AVIO_H + +/** + * @file + * @ingroup lavf_io + * Buffered I/O operations + */ + +#include + +#include "libavutil/common.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "libavformat/version.h" + + +#define AVIO_SEEKABLE_NORMAL 0x0001 /**< Seeking works like for a local file */ + +/** + * Callback for checking whether to abort blocking functions. + * AVERROR_EXIT is returned in this case by the interrupted + * function. During blocking operations, callback is called with + * opaque as parameter. If the callback returns 1, the + * blocking operation will be aborted. + * + * No members can be added to this struct without a major bump, if + * new elements have been added after this struct in AVFormatContext + * or AVIOContext. + */ +typedef struct AVIOInterruptCB { + int (*callback)(void*); + void *opaque; +} AVIOInterruptCB; + +/** + * Bytestream IO Context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVIOContext) must not be used outside libav*. + * + * @note None of the function pointers in AVIOContext should be called + * directly, they should only be set by the client application + * when implementing custom I/O. Normally these are set to the + * function pointers specified in avio_alloc_context() + */ +typedef struct AVIOContext { + /** + * A class for private options. + * + * If this AVIOContext is created by avio_open2(), av_class is set and + * passes the options down to protocols. + * + * If this AVIOContext is manually allocated, then av_class may be set by + * the caller. + * + * warning -- this field can be NULL, be sure to not pass this AVIOContext + * to any av_opt_* functions in that case. + */ + const AVClass *av_class; + unsigned char *buffer; /**< Start of the buffer. */ + int buffer_size; /**< Maximum buffer size */ + unsigned char *buf_ptr; /**< Current position in the buffer */ + unsigned char *buf_end; /**< End of the data, may be less than + buffer+buffer_size if the read function returned + less data than requested, e.g. for streams where + no more data has been received yet. */ + void *opaque; /**< A private pointer, passed to the read/write/seek/... + functions. */ + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); + int64_t (*seek)(void *opaque, int64_t offset, int whence); + int64_t pos; /**< position in the file of the current buffer */ + int must_flush; /**< true if the next seek should flush */ + int eof_reached; /**< true if eof reached */ + int write_flag; /**< true if open for writing */ + int max_packet_size; + unsigned long checksum; + unsigned char *checksum_ptr; + unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); + int error; /**< contains the error code or 0 if no error happened */ + /** + * Pause or resume playback for network streaming protocols - e.g. MMS. + */ + int (*read_pause)(void *opaque, int pause); + /** + * Seek to a given timestamp in stream with the specified stream_index. + * Needed for some network streaming protocols which don't support seeking + * to byte position. + */ + int64_t (*read_seek)(void *opaque, int stream_index, + int64_t timestamp, int flags); + /** + * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable. + */ + int seekable; + + /** + * max filesize, used to limit allocations + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t maxsize; + + /** + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ + int direct; + + /** + * Bytes read statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t bytes_read; + + /** + * seek statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int seek_count; + + /** + * writeout statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int writeout_count; +} AVIOContext; + +/* unbuffered I/O */ + +/** + * Return AVIO_FLAG_* access flags corresponding to the access permissions + * of the resource in url, or a negative value corresponding to an + * AVERROR code in case of failure. The returned access flags are + * masked by the value in flags. + * + * @note This function is intrinsically unsafe, in the sense that the + * checked resource may change its existence or permission status from + * one call to another. Thus you should not trust the returned value, + * unless you are sure that no other processes are accessing the + * checked resource. + */ +int avio_check(const char *url, int flags); + +/** + * Allocate and initialize an AVIOContext for buffered I/O. It must be later + * freed with av_free(). + * + * @param buffer Memory block for input/output operations via AVIOContext. + * The buffer must be allocated with av_malloc() and friends. + * @param buffer_size The buffer size is very important for performance. + * For protocols with fixed blocksize it should be set to this blocksize. + * For others a typical size is a cache page, e.g. 4kb. + * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise. + * @param opaque An opaque pointer to user-specific data. + * @param read_packet A function for refilling the buffer, may be NULL. + * @param write_packet A function for writing the buffer contents, may be NULL. + * The function may not change the input buffers content. + * @param seek A function for seeking to specified byte position, may be NULL. + * + * @return Allocated AVIOContext or NULL on failure. + */ +AVIOContext *avio_alloc_context( + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)); + +void avio_w8(AVIOContext *s, int b); +void avio_write(AVIOContext *s, const unsigned char *buf, int size); +void avio_wl64(AVIOContext *s, uint64_t val); +void avio_wb64(AVIOContext *s, uint64_t val); +void avio_wl32(AVIOContext *s, unsigned int val); +void avio_wb32(AVIOContext *s, unsigned int val); +void avio_wl24(AVIOContext *s, unsigned int val); +void avio_wb24(AVIOContext *s, unsigned int val); +void avio_wl16(AVIOContext *s, unsigned int val); +void avio_wb16(AVIOContext *s, unsigned int val); + +/** + * Write a NULL-terminated string. + * @return number of bytes written. + */ +int avio_put_str(AVIOContext *s, const char *str); + +/** + * Convert an UTF-8 string to UTF-16LE and write it. + * @return number of bytes written. + */ +int avio_put_str16le(AVIOContext *s, const char *str); + +/** + * Passing this as the "whence" parameter to a seek function causes it to + * return the filesize without seeking anywhere. Supporting this is optional. + * If it is not supported then the seek function will return <0. + */ +#define AVSEEK_SIZE 0x10000 + +/** + * Oring this flag as into the "whence" parameter to a seek function causes it to + * seek by any means (like reopening and linear reading) or other normally unreasonable + * means that can be extremely slow. + * This may be ignored by the seek code. + */ +#define AVSEEK_FORCE 0x20000 + +/** + * fseek() equivalent for AVIOContext. + * @return new position or AVERROR. + */ +int64_t avio_seek(AVIOContext *s, int64_t offset, int whence); + +/** + * Skip given number of bytes forward + * @return new position or AVERROR. + */ +int64_t avio_skip(AVIOContext *s, int64_t offset); + +/** + * ftell() equivalent for AVIOContext. + * @return position or AVERROR. + */ +static av_always_inline int64_t avio_tell(AVIOContext *s) +{ + return avio_seek(s, 0, SEEK_CUR); +} + +/** + * Get the filesize. + * @return filesize or AVERROR + */ +int64_t avio_size(AVIOContext *s); + +/** + * feof() equivalent for AVIOContext. + * @return non zero if and only if end of file + */ +int url_feof(AVIOContext *s); + +/** @warning currently size is limited */ +int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); + +/** + * Force flushing of buffered data to the output s. + * + * Force the buffered data to be immediately written to the output, + * without to wait to fill the internal buffer. + */ +void avio_flush(AVIOContext *s); + +/** + * Read size bytes from AVIOContext into buf. + * @return number of bytes read or AVERROR + */ +int avio_read(AVIOContext *s, unsigned char *buf, int size); + +/** + * @name Functions for reading from AVIOContext + * @{ + * + * @note return 0 if EOF, so you cannot use it if EOF handling is + * necessary + */ +int avio_r8 (AVIOContext *s); +unsigned int avio_rl16(AVIOContext *s); +unsigned int avio_rl24(AVIOContext *s); +unsigned int avio_rl32(AVIOContext *s); +uint64_t avio_rl64(AVIOContext *s); +unsigned int avio_rb16(AVIOContext *s); +unsigned int avio_rb24(AVIOContext *s); +unsigned int avio_rb32(AVIOContext *s); +uint64_t avio_rb64(AVIOContext *s); +/** + * @} + */ + +/** + * Read a string from pb into buf. The reading will terminate when either + * a NULL character was encountered, maxlen bytes have been read, or nothing + * more can be read from pb. The result is guaranteed to be NULL-terminated, it + * will be truncated if buf is too small. + * Note that the string is not interpreted or validated in any way, it + * might get truncated in the middle of a sequence for multi-byte encodings. + * + * @return number of bytes read (is always <= maxlen). + * If reading ends on EOF or error, the return value will be one more than + * bytes actually read. + */ +int avio_get_str(AVIOContext *pb, int maxlen, char *buf, int buflen); + +/** + * Read a UTF-16 string from pb and convert it to UTF-8. + * The reading will terminate when either a null or invalid character was + * encountered or maxlen bytes have been read. + * @return number of bytes read (is always <= maxlen) + */ +int avio_get_str16le(AVIOContext *pb, int maxlen, char *buf, int buflen); +int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); + + +/** + * @name URL open modes + * The flags argument to avio_open must be one of the following + * constants, optionally ORed with other flags. + * @{ + */ +#define AVIO_FLAG_READ 1 /**< read-only */ +#define AVIO_FLAG_WRITE 2 /**< write-only */ +#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE) /**< read-write pseudo flag */ +/** + * @} + */ + +/** + * Use non-blocking mode. + * If this flag is set, operations on the context will return + * AVERROR(EAGAIN) if they can not be performed immediately. + * If this flag is not set, operations on the context will never return + * AVERROR(EAGAIN). + * Note that this flag does not affect the opening/connecting of the + * context. Connecting a protocol will always block if necessary (e.g. on + * network protocols) but never hang (e.g. on busy devices). + * Warning: non-blocking protocols is work-in-progress; this flag may be + * silently ignored. + */ +#define AVIO_FLAG_NONBLOCK 8 + +/** + * Use direct mode. + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ +#define AVIO_FLAG_DIRECT 0x8000 + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param flags flags which control how the resource indicated by url + * is to be opened + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open(AVIOContext **s, const char *url, int flags); + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param flags flags which control how the resource indicated by url + * is to be opened + * @param int_cb an interrupt callback to be used at the protocols level + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dict containing options + * that were not found. May be NULL. + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open2(AVIOContext **s, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options); + +/** + * Close the resource accessed by the AVIOContext s and free it. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_closep + */ +int avio_close(AVIOContext *s); + +/** + * Close the resource accessed by the AVIOContext *s, free it + * and set the pointer pointing to it to NULL. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_close + */ +int avio_closep(AVIOContext **s); + + +/** + * Open a write only memory stream. + * + * @param s new IO context + * @return zero if no error. + */ +int avio_open_dyn_buf(AVIOContext **s); + +/** + * Return the written size and a pointer to the buffer. The buffer + * must be freed with av_free(). + * Padding of FF_INPUT_BUFFER_PADDING_SIZE is added to the buffer. + * + * @param s IO context + * @param pbuffer pointer to a byte buffer + * @return the length of the byte buffer + */ +int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer); + +/** + * Iterate through names of available protocols. + * + * @param opaque A private pointer representing current protocol. + * It must be a pointer to NULL on first iteration and will + * be updated by successive calls to avio_enum_protocols. + * @param output If set to 1, iterate over output protocols, + * otherwise over input protocols. + * + * @return A static string containing the name of current protocol or NULL + */ +const char *avio_enum_protocols(void **opaque, int output); + +/** + * Pause and resume playing - only meaningful if using a network streaming + * protocol (e.g. MMS). + * @param pause 1 for pause, 0 for resume + */ +int avio_pause(AVIOContext *h, int pause); + +/** + * Seek to a given timestamp relative to some component stream. + * Only meaningful if using a network streaming protocol (e.g. MMS.). + * @param stream_index The stream index that the timestamp is relative to. + * If stream_index is (-1) the timestamp should be in AV_TIME_BASE + * units from the beginning of the presentation. + * If a stream_index >= 0 is used and the protocol does not support + * seeking based on component streams, the call will fail. + * @param timestamp timestamp in AVStream.time_base units + * or if there is no stream specified then in AV_TIME_BASE units. + * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE + * and AVSEEK_FLAG_ANY. The protocol may silently ignore + * AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will + * fail if used and not supported. + * @return >= 0 on success + * @see AVInputFormat::read_seek + */ +int64_t avio_seek_time(AVIOContext *h, int stream_index, + int64_t timestamp, int flags); + +#endif /* AVFORMAT_AVIO_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avio.o Binary file ffmpeg/libavformat/avio.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avio_internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avio_internal.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,107 @@ +/* + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVIO_INTERNAL_H +#define AVFORMAT_AVIO_INTERNAL_H + +#include "avio.h" +#include "url.h" + +#include "libavutil/log.h" + +extern const AVClass ffio_url_class; + +int ffio_init_context(AVIOContext *s, + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)); + + +/** + * Read size bytes from AVIOContext into buf. + * This reads at most 1 packet. If that is not enough fewer bytes will be + * returned. + * @return number of bytes read or AVERROR + */ +int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size); + +void ffio_fill(AVIOContext *s, int b, int count); + +static av_always_inline void ffio_wfourcc(AVIOContext *pb, const uint8_t *s) +{ + avio_wl32(pb, MKTAG(s[0], s[1], s[2], s[3])); +} + +/** + * Rewind the AVIOContext using the specified buffer containing the first buf_size bytes of the file. + * Used after probing to avoid seeking. + * Joins buf and s->buffer, taking any overlap into consideration. + * @note s->buffer must overlap with buf or they can't be joined and the function fails + * + * @param s The read-only AVIOContext to rewind + * @param buf The probe buffer containing the first buf_size bytes of the file + * @param buf_size The size of buf + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **buf, int buf_size); + +uint64_t ffio_read_varlen(AVIOContext *bc); + +/** @warning must be called before any I/O */ +int ffio_set_buf_size(AVIOContext *s, int buf_size); + +int ffio_limit(AVIOContext *s, int size); + +void ffio_init_checksum(AVIOContext *s, + unsigned long (*update_checksum)(unsigned long c, const uint8_t *p, unsigned int len), + unsigned long checksum); +unsigned long ffio_get_checksum(AVIOContext *s); +unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf, + unsigned int len); + +/** + * Open a write only packetized memory stream with a maximum packet + * size of 'max_packet_size'. The stream is stored in a memory buffer + * with a big-endian 4 byte header giving the packet size in bytes. + * + * @param s new IO context + * @param max_packet_size maximum packet size (must be > 0) + * @return zero if no error. + */ +int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); + +/** + * Create and initialize a AVIOContext for accessing the + * resource referenced by the URLContext h. + * @note When the URLContext h has been opened in read+write mode, the + * AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @return 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int ffio_fdopen(AVIOContext **s, URLContext *h); + +#endif /* AVFORMAT_AVIO_INTERNAL_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aviobuf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aviobuf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1008 @@ +/* + * buffered I/O + * Copyright (c) 2000,2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/crc.h" +#include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "avformat.h" +#include "avio.h" +#include "avio_internal.h" +#include "internal.h" +#include "url.h" +#include + +#define IO_BUFFER_SIZE 32768 + +/** + * Do seeks within this distance ahead of the current buffer by skipping + * data instead of calling the protocol seek function, for seekable + * protocols. + */ +#define SHORT_SEEK_THRESHOLD 4096 + +static void *ffio_url_child_next(void *obj, void *prev) +{ + AVIOContext *s = obj; + return prev ? NULL : s->opaque; +} + +static const AVClass *ffio_url_child_class_next(const AVClass *prev) +{ + return prev ? NULL : &ffurl_context_class; +} + +static const AVOption ffio_url_options[] = { + { NULL }, +}; + +const AVClass ffio_url_class = { + .class_name = "AVIOContext", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .option = ffio_url_options, + .child_next = ffio_url_child_next, + .child_class_next = ffio_url_child_class_next, +}; + +static void fill_buffer(AVIOContext *s); +static int url_resetbuf(AVIOContext *s, int flags); + +int ffio_init_context(AVIOContext *s, + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)) +{ + s->buffer = buffer; + s->buffer_size = buffer_size; + s->buf_ptr = buffer; + s->opaque = opaque; + s->direct = 0; + + url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ); + + s->write_packet = write_packet; + s->read_packet = read_packet; + s->seek = seek; + s->pos = 0; + s->must_flush = 0; + s->eof_reached = 0; + s->error = 0; + s->seekable = AVIO_SEEKABLE_NORMAL; + s->max_packet_size = 0; + s->update_checksum = NULL; + + if (!read_packet && !write_flag) { + s->pos = buffer_size; + s->buf_end = s->buffer + buffer_size; + } + s->read_pause = NULL; + s->read_seek = NULL; + + return 0; +} + +AVIOContext *avio_alloc_context( + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)) +{ + AVIOContext *s = av_mallocz(sizeof(AVIOContext)); + if (!s) + return NULL; + ffio_init_context(s, buffer, buffer_size, write_flag, opaque, + read_packet, write_packet, seek); + return s; +} + +static void writeout(AVIOContext *s, const uint8_t *data, int len) +{ + if (s->write_packet && !s->error) { + int ret = s->write_packet(s->opaque, (uint8_t *)data, len); + if (ret < 0) { + s->error = ret; + } + } + s->writeout_count ++; + s->pos += len; +} + +static void flush_buffer(AVIOContext *s) +{ + if (s->buf_ptr > s->buffer) { + writeout(s, s->buffer, s->buf_ptr - s->buffer); + if (s->update_checksum) { + s->checksum = s->update_checksum(s->checksum, s->checksum_ptr, + s->buf_ptr - s->checksum_ptr); + s->checksum_ptr = s->buffer; + } + } + s->buf_ptr = s->buffer; +} + +void avio_w8(AVIOContext *s, int b) +{ + av_assert2(b>=-128 && b<=255); + *s->buf_ptr++ = b; + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); +} + +void ffio_fill(AVIOContext *s, int b, int count) +{ + while (count > 0) { + int len = FFMIN(s->buf_end - s->buf_ptr, count); + memset(s->buf_ptr, b, len); + s->buf_ptr += len; + + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); + + count -= len; + } +} + +void avio_write(AVIOContext *s, const unsigned char *buf, int size) +{ + if (s->direct && !s->update_checksum) { + avio_flush(s); + writeout(s, buf, size); + return; + } + while (size > 0) { + int len = FFMIN(s->buf_end - s->buf_ptr, size); + memcpy(s->buf_ptr, buf, len); + s->buf_ptr += len; + + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); + + buf += len; + size -= len; + } +} + +void avio_flush(AVIOContext *s) +{ + flush_buffer(s); + s->must_flush = 0; +} + +int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) +{ + int64_t offset1; + int64_t pos; + int force = whence & AVSEEK_FORCE; + whence &= ~AVSEEK_FORCE; + + if(!s) + return AVERROR(EINVAL); + + pos = s->pos - (s->write_flag ? 0 : (s->buf_end - s->buffer)); + + if (whence != SEEK_CUR && whence != SEEK_SET) + return AVERROR(EINVAL); + + if (whence == SEEK_CUR) { + offset1 = pos + (s->buf_ptr - s->buffer); + if (offset == 0) + return offset1; + offset += offset1; + } + offset1 = offset - pos; + if (!s->must_flush && (!s->direct || !s->seek) && + offset1 >= 0 && offset1 <= (s->buf_end - s->buffer)) { + /* can do the seek inside the buffer */ + s->buf_ptr = s->buffer + offset1; + } else if ((!s->seekable || + offset1 <= s->buf_end + SHORT_SEEK_THRESHOLD - s->buffer) && + !s->write_flag && offset1 >= 0 && + (!s->direct || !s->seek) && + (whence != SEEK_END || force)) { + while(s->pos < offset && !s->eof_reached) + fill_buffer(s); + if (s->eof_reached) + return AVERROR_EOF; + s->buf_ptr = s->buf_end + offset - s->pos; + } else { + int64_t res; + + if (s->write_flag) { + flush_buffer(s); + s->must_flush = 1; + } + if (!s->seek) + return AVERROR(EPIPE); + if ((res = s->seek(s->opaque, offset, SEEK_SET)) < 0) + return res; + s->seek_count ++; + if (!s->write_flag) + s->buf_end = s->buffer; + s->buf_ptr = s->buffer; + s->pos = offset; + } + s->eof_reached = 0; + return offset; +} + +int64_t avio_skip(AVIOContext *s, int64_t offset) +{ + return avio_seek(s, offset, SEEK_CUR); +} + +int64_t avio_size(AVIOContext *s) +{ + int64_t size; + + if (!s) + return AVERROR(EINVAL); + + if (!s->seek) + return AVERROR(ENOSYS); + size = s->seek(s->opaque, 0, AVSEEK_SIZE); + if (size < 0) { + if ((size = s->seek(s->opaque, -1, SEEK_END)) < 0) + return size; + size++; + s->seek(s->opaque, s->pos, SEEK_SET); + } + return size; +} + +int url_feof(AVIOContext *s) +{ + if(!s) + return 0; + if(s->eof_reached){ + s->eof_reached=0; + fill_buffer(s); + } + return s->eof_reached; +} + +void avio_wl32(AVIOContext *s, unsigned int val) +{ + avio_w8(s, (uint8_t) val ); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, val >> 24 ); +} + +void avio_wb32(AVIOContext *s, unsigned int val) +{ + avio_w8(s, val >> 24 ); + avio_w8(s, (uint8_t)(val >> 16)); + avio_w8(s, (uint8_t)(val >> 8 )); + avio_w8(s, (uint8_t) val ); +} + +int avio_put_str(AVIOContext *s, const char *str) +{ + int len = 1; + if (str) { + len += strlen(str); + avio_write(s, (const unsigned char *) str, len); + } else + avio_w8(s, 0); + return len; +} + +int avio_put_str16le(AVIOContext *s, const char *str) +{ + const uint8_t *q = str; + int ret = 0; + + while (*q) { + uint32_t ch; + uint16_t tmp; + + GET_UTF8(ch, *q++, break;) + PUT_UTF16(ch, tmp, avio_wl16(s, tmp); ret += 2;) + } + avio_wl16(s, 0); + ret += 2; + return ret; +} + +int ff_get_v_length(uint64_t val) +{ + int i = 1; + + while (val >>= 7) + i++; + + return i; +} + +void ff_put_v(AVIOContext *bc, uint64_t val) +{ + int i = ff_get_v_length(val); + + while (--i > 0) + avio_w8(bc, 128 | (uint8_t)(val >> (7*i))); + + avio_w8(bc, val & 127); +} + +void avio_wl64(AVIOContext *s, uint64_t val) +{ + avio_wl32(s, (uint32_t)(val & 0xffffffff)); + avio_wl32(s, (uint32_t)(val >> 32)); +} + +void avio_wb64(AVIOContext *s, uint64_t val) +{ + avio_wb32(s, (uint32_t)(val >> 32)); + avio_wb32(s, (uint32_t)(val & 0xffffffff)); +} + +void avio_wl16(AVIOContext *s, unsigned int val) +{ + avio_w8(s, (uint8_t)val); + avio_w8(s, (int)val >> 8); +} + +void avio_wb16(AVIOContext *s, unsigned int val) +{ + avio_w8(s, (int)val >> 8); + avio_w8(s, (uint8_t)val); +} + +void avio_wl24(AVIOContext *s, unsigned int val) +{ + avio_wl16(s, val & 0xffff); + avio_w8(s, (int)val >> 16); +} + +void avio_wb24(AVIOContext *s, unsigned int val) +{ + avio_wb16(s, (int)val >> 8); + avio_w8(s, (uint8_t)val); +} + +/* Input stream */ + +static void fill_buffer(AVIOContext *s) +{ + uint8_t *dst = !s->max_packet_size && + s->buf_end - s->buffer < s->buffer_size ? + s->buf_end : s->buffer; + int len = s->buffer_size - (dst - s->buffer); + int max_buffer_size = s->max_packet_size ? + s->max_packet_size : IO_BUFFER_SIZE; + + /* can't fill the buffer without read_packet, just set EOF if appropriate */ + if (!s->read_packet && s->buf_ptr >= s->buf_end) + s->eof_reached = 1; + + /* no need to do anything if EOF already reached */ + if (s->eof_reached) + return; + + if (s->update_checksum && dst == s->buffer) { + if (s->buf_end > s->checksum_ptr) + s->checksum = s->update_checksum(s->checksum, s->checksum_ptr, + s->buf_end - s->checksum_ptr); + s->checksum_ptr = s->buffer; + } + + /* make buffer smaller in case it ended up large after probing */ + if (s->read_packet && s->buffer_size > max_buffer_size) { + ffio_set_buf_size(s, max_buffer_size); + + s->checksum_ptr = dst = s->buffer; + len = s->buffer_size; + } + + if (s->read_packet) + len = s->read_packet(s->opaque, dst, len); + else + len = 0; + if (len <= 0) { + /* do not modify buffer if EOF reached so that a seek back can + be done without rereading data */ + s->eof_reached = 1; + if (len < 0) + s->error = len; + } else { + s->pos += len; + s->buf_ptr = dst; + s->buf_end = dst + len; + s->bytes_read += len; + } +} + +unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf, + unsigned int len) +{ + return av_crc(av_crc_get_table(AV_CRC_32_IEEE), checksum, buf, len); +} + +unsigned long ffio_get_checksum(AVIOContext *s) +{ + s->checksum = s->update_checksum(s->checksum, s->checksum_ptr, + s->buf_ptr - s->checksum_ptr); + s->update_checksum = NULL; + return s->checksum; +} + +void ffio_init_checksum(AVIOContext *s, + unsigned long (*update_checksum)(unsigned long c, const uint8_t *p, unsigned int len), + unsigned long checksum) +{ + s->update_checksum = update_checksum; + if (s->update_checksum) { + s->checksum = checksum; + s->checksum_ptr = s->buf_ptr; + } +} + +/* XXX: put an inline version */ +int avio_r8(AVIOContext *s) +{ + if (s->buf_ptr >= s->buf_end) + fill_buffer(s); + if (s->buf_ptr < s->buf_end) + return *s->buf_ptr++; + return 0; +} + +int avio_read(AVIOContext *s, unsigned char *buf, int size) +{ + int len, size1; + + size1 = size; + while (size > 0) { + len = s->buf_end - s->buf_ptr; + if (len > size) + len = size; + if (len == 0 || s->write_flag) { + if((s->direct || size > s->buffer_size) && !s->update_checksum){ + if(s->read_packet) + len = s->read_packet(s->opaque, buf, size); + if (len <= 0) { + /* do not modify buffer if EOF reached so that a seek back can + be done without rereading data */ + s->eof_reached = 1; + if(len<0) + s->error= len; + break; + } else { + s->pos += len; + s->bytes_read += len; + size -= len; + buf += len; + s->buf_ptr = s->buffer; + s->buf_end = s->buffer/* + len*/; + } + } else { + fill_buffer(s); + len = s->buf_end - s->buf_ptr; + if (len == 0) + break; + } + } else { + memcpy(buf, s->buf_ptr, len); + buf += len; + s->buf_ptr += len; + size -= len; + } + } + if (size1 == size) { + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; + } + return size1 - size; +} + +int ffio_read_partial(AVIOContext *s, unsigned char *buf, int size) +{ + int len; + + if (size < 0) + return -1; + + if (s->read_packet && s->write_flag) { + len = s->read_packet(s->opaque, buf, size); + if (len > 0) + s->pos += len; + return len; + } + + len = s->buf_end - s->buf_ptr; + if (len == 0) { + /* Reset the buf_end pointer to the start of the buffer, to make sure + * the fill_buffer call tries to read as much data as fits into the + * full buffer, instead of just what space is left after buf_end. + * This avoids returning partial packets at the end of the buffer, + * for packet based inputs. + */ + s->buf_end = s->buf_ptr = s->buffer; + fill_buffer(s); + len = s->buf_end - s->buf_ptr; + } + if (len > size) + len = size; + memcpy(buf, s->buf_ptr, len); + s->buf_ptr += len; + if (!len) { + if (s->error) return s->error; + if (url_feof(s)) return AVERROR_EOF; + } + return len; +} + +unsigned int avio_rl16(AVIOContext *s) +{ + unsigned int val; + val = avio_r8(s); + val |= avio_r8(s) << 8; + return val; +} + +unsigned int avio_rl24(AVIOContext *s) +{ + unsigned int val; + val = avio_rl16(s); + val |= avio_r8(s) << 16; + return val; +} + +unsigned int avio_rl32(AVIOContext *s) +{ + unsigned int val; + val = avio_rl16(s); + val |= avio_rl16(s) << 16; + return val; +} + +uint64_t avio_rl64(AVIOContext *s) +{ + uint64_t val; + val = (uint64_t)avio_rl32(s); + val |= (uint64_t)avio_rl32(s) << 32; + return val; +} + +unsigned int avio_rb16(AVIOContext *s) +{ + unsigned int val; + val = avio_r8(s) << 8; + val |= avio_r8(s); + return val; +} + +unsigned int avio_rb24(AVIOContext *s) +{ + unsigned int val; + val = avio_rb16(s) << 8; + val |= avio_r8(s); + return val; +} +unsigned int avio_rb32(AVIOContext *s) +{ + unsigned int val; + val = avio_rb16(s) << 16; + val |= avio_rb16(s); + return val; +} + +int ff_get_line(AVIOContext *s, char *buf, int maxlen) +{ + int i = 0; + char c; + + do { + c = avio_r8(s); + if (c && i < maxlen-1) + buf[i++] = c; + } while (c != '\n' && c); + + buf[i] = 0; + return i; +} + +int avio_get_str(AVIOContext *s, int maxlen, char *buf, int buflen) +{ + int i; + + if (buflen <= 0) + return AVERROR(EINVAL); + // reserve 1 byte for terminating 0 + buflen = FFMIN(buflen - 1, maxlen); + for (i = 0; i < buflen; i++) + if (!(buf[i] = avio_r8(s))) + return i + 1; + buf[i] = 0; + for (; i < maxlen; i++) + if (!avio_r8(s)) + return i + 1; + return maxlen; +} + +#define GET_STR16(type, read) \ + int avio_get_str16 ##type(AVIOContext *pb, int maxlen, char *buf, int buflen)\ +{\ + char* q = buf;\ + int ret = 0;\ + if (buflen <= 0) \ + return AVERROR(EINVAL); \ + while (ret + 1 < maxlen) {\ + uint8_t tmp;\ + uint32_t ch;\ + GET_UTF16(ch, (ret += 2) <= maxlen ? read(pb) : 0, break;)\ + if (!ch)\ + break;\ + PUT_UTF8(ch, tmp, if (q - buf < buflen - 1) *q++ = tmp;)\ + }\ + *q = 0;\ + return ret;\ +}\ + +GET_STR16(le, avio_rl16) +GET_STR16(be, avio_rb16) + +#undef GET_STR16 + +uint64_t avio_rb64(AVIOContext *s) +{ + uint64_t val; + val = (uint64_t)avio_rb32(s) << 32; + val |= (uint64_t)avio_rb32(s); + return val; +} + +uint64_t ffio_read_varlen(AVIOContext *bc){ + uint64_t val = 0; + int tmp; + + do{ + tmp = avio_r8(bc); + val= (val<<7) + (tmp&127); + }while(tmp&128); + return val; +} + +int ffio_fdopen(AVIOContext **s, URLContext *h) +{ + uint8_t *buffer; + int buffer_size, max_packet_size; + + max_packet_size = h->max_packet_size; + if (max_packet_size) { + buffer_size = max_packet_size; /* no need to bufferize more than one packet */ + } else { + buffer_size = IO_BUFFER_SIZE; + } + buffer = av_malloc(buffer_size); + if (!buffer) + return AVERROR(ENOMEM); + + *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, + (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek); + if (!*s) { + av_free(buffer); + return AVERROR(ENOMEM); + } + (*s)->direct = h->flags & AVIO_FLAG_DIRECT; + (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; + (*s)->max_packet_size = max_packet_size; + if(h->prot) { + (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause; + (*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek; + } + (*s)->av_class = &ffio_url_class; + return 0; +} + +int ffio_set_buf_size(AVIOContext *s, int buf_size) +{ + uint8_t *buffer; + buffer = av_malloc(buf_size); + if (!buffer) + return AVERROR(ENOMEM); + + av_free(s->buffer); + s->buffer = buffer; + s->buffer_size = buf_size; + s->buf_ptr = buffer; + url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ); + return 0; +} + +static int url_resetbuf(AVIOContext *s, int flags) +{ + av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ); + + if (flags & AVIO_FLAG_WRITE) { + s->buf_end = s->buffer + s->buffer_size; + s->write_flag = 1; + } else { + s->buf_end = s->buffer; + s->write_flag = 0; + } + return 0; +} + +int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **bufp, int buf_size) +{ + int64_t buffer_start; + int buffer_size; + int overlap, new_size, alloc_size; + uint8_t *buf = *bufp; + + if (s->write_flag) { + av_freep(bufp); + return AVERROR(EINVAL); + } + + buffer_size = s->buf_end - s->buffer; + + /* the buffers must touch or overlap */ + if ((buffer_start = s->pos - buffer_size) > buf_size) { + av_freep(bufp); + return AVERROR(EINVAL); + } + + overlap = buf_size - buffer_start; + new_size = buf_size + buffer_size - overlap; + + alloc_size = FFMAX(s->buffer_size, new_size); + if (alloc_size > buf_size) + if (!(buf = (*bufp) = av_realloc_f(buf, 1, alloc_size))) + return AVERROR(ENOMEM); + + if (new_size > buf_size) { + memcpy(buf + buf_size, s->buffer + overlap, buffer_size - overlap); + buf_size = new_size; + } + + av_free(s->buffer); + s->buf_ptr = s->buffer = buf; + s->buffer_size = alloc_size; + s->pos = buf_size; + s->buf_end = s->buf_ptr + buf_size; + s->eof_reached = 0; + s->must_flush = 0; + + return 0; +} + +int avio_open(AVIOContext **s, const char *filename, int flags) +{ + return avio_open2(s, filename, flags, NULL, NULL); +} + +int avio_open2(AVIOContext **s, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + URLContext *h; + int err; + + err = ffurl_open(&h, filename, flags, int_cb, options); + if (err < 0) + return err; + err = ffio_fdopen(s, h); + if (err < 0) { + ffurl_close(h); + return err; + } + return 0; +} + +int avio_close(AVIOContext *s) +{ + URLContext *h; + + if (!s) + return 0; + + avio_flush(s); + h = s->opaque; + av_freep(&s->buffer); + if (s->write_flag) + av_log(s, AV_LOG_DEBUG, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count); + else + av_log(s, AV_LOG_DEBUG, "Statistics: %"PRId64" bytes read, %d seeks\n", s->bytes_read, s->seek_count); + av_free(s); + return ffurl_close(h); +} + +int avio_closep(AVIOContext **s) +{ + int ret = avio_close(*s); + *s = NULL; + return ret; +} + +int avio_printf(AVIOContext *s, const char *fmt, ...) +{ + va_list ap; + char buf[4096]; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + avio_write(s, buf, strlen(buf)); + return ret; +} + +int avio_pause(AVIOContext *s, int pause) +{ + if (!s->read_pause) + return AVERROR(ENOSYS); + return s->read_pause(s->opaque, pause); +} + +int64_t avio_seek_time(AVIOContext *s, int stream_index, + int64_t timestamp, int flags) +{ + URLContext *h = s->opaque; + int64_t ret; + if (!s->read_seek) + return AVERROR(ENOSYS); + ret = s->read_seek(h, stream_index, timestamp, flags); + if (ret >= 0) { + int64_t pos; + s->buf_ptr = s->buf_end; // Flush buffer + pos = s->seek(h, 0, SEEK_CUR); + if (pos >= 0) + s->pos = pos; + else if (pos != AVERROR(ENOSYS)) + ret = pos; + } + return ret; +} + +/* output in a dynamic buffer */ + +typedef struct DynBuffer { + int pos, size, allocated_size; + uint8_t *buffer; + int io_buffer_size; + uint8_t io_buffer[1]; +} DynBuffer; + +static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size) +{ + DynBuffer *d = opaque; + unsigned new_size, new_allocated_size; + + /* reallocate buffer if needed */ + new_size = d->pos + buf_size; + new_allocated_size = d->allocated_size; + if (new_size < d->pos || new_size > INT_MAX/2) + return -1; + while (new_size > new_allocated_size) { + if (!new_allocated_size) + new_allocated_size = new_size; + else + new_allocated_size += new_allocated_size / 2 + 1; + } + + if (new_allocated_size > d->allocated_size) { + d->buffer = av_realloc_f(d->buffer, 1, new_allocated_size); + if(d->buffer == NULL) + return AVERROR(ENOMEM); + d->allocated_size = new_allocated_size; + } + memcpy(d->buffer + d->pos, buf, buf_size); + d->pos = new_size; + if (d->pos > d->size) + d->size = d->pos; + return buf_size; +} + +static int dyn_packet_buf_write(void *opaque, uint8_t *buf, int buf_size) +{ + unsigned char buf1[4]; + int ret; + + /* packetized write: output the header */ + AV_WB32(buf1, buf_size); + ret = dyn_buf_write(opaque, buf1, 4); + if (ret < 0) + return ret; + + /* then the data */ + return dyn_buf_write(opaque, buf, buf_size); +} + +static int64_t dyn_buf_seek(void *opaque, int64_t offset, int whence) +{ + DynBuffer *d = opaque; + + if (whence == SEEK_CUR) + offset += d->pos; + else if (whence == SEEK_END) + offset += d->size; + if (offset < 0 || offset > 0x7fffffffLL) + return -1; + d->pos = offset; + return 0; +} + +static int url_open_dyn_buf_internal(AVIOContext **s, int max_packet_size) +{ + DynBuffer *d; + unsigned io_buffer_size = max_packet_size ? max_packet_size : 1024; + + if (sizeof(DynBuffer) + io_buffer_size < io_buffer_size) + return -1; + d = av_mallocz(sizeof(DynBuffer) + io_buffer_size); + if (!d) + return AVERROR(ENOMEM); + d->io_buffer_size = io_buffer_size; + *s = avio_alloc_context(d->io_buffer, d->io_buffer_size, 1, d, NULL, + max_packet_size ? dyn_packet_buf_write : dyn_buf_write, + max_packet_size ? NULL : dyn_buf_seek); + if(!*s) { + av_free(d); + return AVERROR(ENOMEM); + } + (*s)->max_packet_size = max_packet_size; + return 0; +} + +int avio_open_dyn_buf(AVIOContext **s) +{ + return url_open_dyn_buf_internal(s, 0); +} + +int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size) +{ + if (max_packet_size <= 0) + return -1; + return url_open_dyn_buf_internal(s, max_packet_size); +} + +int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer) +{ + DynBuffer *d = s->opaque; + int size; + static const char padbuf[FF_INPUT_BUFFER_PADDING_SIZE] = {0}; + int padding = 0; + + /* don't attempt to pad fixed-size packet buffers */ + if (!s->max_packet_size) { + avio_write(s, padbuf, sizeof(padbuf)); + padding = FF_INPUT_BUFFER_PADDING_SIZE; + } + + avio_flush(s); + + *pbuffer = d->buffer; + size = d->size; + av_free(d); + av_free(s); + return size - padding; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aviobuf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/aviobuf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/aviobuf.o libavformat/aviobuf.o: libavformat/aviobuf.c \ + libavutil/crc.h libavutil/attributes.h libavutil/dict.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/bswap.h \ + config.h libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavutil/log.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/opt.h \ + libavutil/samplefmt.h libavutil/avassert.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/aviobuf.o Binary file ffmpeg/libavformat/aviobuf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avisynth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avisynth.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,639 @@ +/* + * Avi/AvxSynth support + * Copyright (c) 2012 AvxSynth Team. + * + * This file is part of FFmpeg + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavcodec/internal.h" + +// Enable function pointer definitions for runtime loading. +#define AVSC_NO_DECLSPEC + +// Shut up ffmpeg error messages. +// avisynth_c.h contains inline functions that call these functions. +#undef malloc +#undef free +#undef printf + +// Platform-specific directives for AviSynth vs AvxSynth. +#ifdef _WIN32 + #include + #undef EXTERN_C + #include "compat/avisynth/avisynth_c.h" + #define AVISYNTH_LIB "avisynth" +#else + #include + #include "compat/avisynth/avxsynth_c.h" + #if defined (__APPLE__) + #define AVISYNTH_LIB "libavxsynth.dylib" + #else + #define AVISYNTH_LIB "libavxsynth.so" + #endif + + #define LoadLibrary(x) dlopen(x, RTLD_NOW | RTLD_GLOBAL) + #define GetProcAddress dlsym + #define FreeLibrary dlclose +#endif + +// AvxSynth doesn't have these colorspaces, so disable them +#ifndef _WIN32 +#define avs_is_yv24(vi) 0 +#define avs_is_yv16(vi) 0 +#define avs_is_yv411(vi) 0 +#define avs_is_y8(vi) 0 +#endif + +typedef struct { + void *library; +#define AVSC_DECLARE_FUNC(name) name##_func name + AVSC_DECLARE_FUNC(avs_create_script_environment); + AVSC_DECLARE_FUNC(avs_delete_script_environment); + AVSC_DECLARE_FUNC(avs_get_error); + AVSC_DECLARE_FUNC(avs_clip_get_error); + AVSC_DECLARE_FUNC(avs_invoke); + AVSC_DECLARE_FUNC(avs_release_value); + AVSC_DECLARE_FUNC(avs_get_video_info); + AVSC_DECLARE_FUNC(avs_take_clip); + AVSC_DECLARE_FUNC(avs_release_clip); + AVSC_DECLARE_FUNC(avs_bit_blt); + AVSC_DECLARE_FUNC(avs_get_audio); + AVSC_DECLARE_FUNC(avs_get_frame); + AVSC_DECLARE_FUNC(avs_release_video_frame); +#undef AVSC_DECLARE_FUNC +} AviSynthLibrary; + +struct AviSynthContext { + AVS_ScriptEnvironment *env; + AVS_Clip *clip; + const AVS_VideoInfo *vi; + + // avisynth_read_packet_video() iterates over this. + int n_planes; + const int *planes; + + int curr_stream; + int curr_frame; + int64_t curr_sample; + + int error; + + // Linked list pointers. + struct AviSynthContext *next; +}; +typedef struct AviSynthContext AviSynthContext; + +static const int avs_planes_packed[1] = {0}; +static const int avs_planes_grey[1] = {AVS_PLANAR_Y}; +static const int avs_planes_yuv[3] = {AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V}; + +// A conflict between C++ global objects, atexit, and dynamic loading requires +// us to register our own atexit handler to prevent double freeing. +static AviSynthLibrary *avs_library = NULL; +static int avs_atexit_called = 0; + +// Linked list of AviSynthContexts. An atexit handler destroys this list. +static AviSynthContext *avs_ctx_list = NULL; + +static av_cold void avisynth_atexit_handler(void); + +static av_cold int avisynth_load_library(void) { + avs_library = av_mallocz(sizeof(AviSynthLibrary)); + if (!avs_library) + return AVERROR_UNKNOWN; + + avs_library->library = LoadLibrary(AVISYNTH_LIB); + if (!avs_library->library) + goto init_fail; + +#define LOAD_AVS_FUNC(name, continue_on_fail) \ +{ \ + avs_library->name = (void*)GetProcAddress(avs_library->library, #name); \ + if(!continue_on_fail && !avs_library->name) \ + goto fail; \ +} + LOAD_AVS_FUNC(avs_create_script_environment, 0); + LOAD_AVS_FUNC(avs_delete_script_environment, 0); + LOAD_AVS_FUNC(avs_get_error, 1); // New to AviSynth 2.6 + LOAD_AVS_FUNC(avs_clip_get_error, 0); + LOAD_AVS_FUNC(avs_invoke, 0); + LOAD_AVS_FUNC(avs_release_value, 0); + LOAD_AVS_FUNC(avs_get_video_info, 0); + LOAD_AVS_FUNC(avs_take_clip, 0); + LOAD_AVS_FUNC(avs_release_clip, 0); + LOAD_AVS_FUNC(avs_bit_blt, 0); + LOAD_AVS_FUNC(avs_get_audio, 0); + LOAD_AVS_FUNC(avs_get_frame, 0); + LOAD_AVS_FUNC(avs_release_video_frame, 0); +#undef LOAD_AVS_FUNC + + atexit(avisynth_atexit_handler); + return 0; + +fail: + FreeLibrary(avs_library->library); +init_fail: + av_freep(&avs_library); + return AVERROR_UNKNOWN; +} + +// Note that avisynth_context_create and avisynth_context_destroy +// do not allocate or free the actual context! That is taken care of +// by libavformat. +static av_cold int avisynth_context_create(AVFormatContext *s) { + AviSynthContext *avs = (AviSynthContext *)s->priv_data; + int ret; + + if (!avs_library) { + if (ret = avisynth_load_library()) + return ret; + } + + avs->env = avs_library->avs_create_script_environment(3); + if (avs_library->avs_get_error) { + const char *error = avs_library->avs_get_error(avs->env); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + return AVERROR_UNKNOWN; + } + } + + if (!avs_ctx_list) { + avs_ctx_list = avs; + } else { + avs->next = avs_ctx_list; + avs_ctx_list = avs; + } + + return 0; +} + +static av_cold void avisynth_context_destroy(AviSynthContext *avs) { + if (avs_atexit_called) + return; + + if (avs == avs_ctx_list) { + avs_ctx_list = avs->next; + } else { + AviSynthContext *prev = avs_ctx_list; + while (prev->next != avs) + prev = prev->next; + prev->next = avs->next; + } + + if (avs->clip) { + avs_library->avs_release_clip(avs->clip); + avs->clip = NULL; + } + if (avs->env) { + avs_library->avs_delete_script_environment(avs->env); + avs->env = NULL; + } +} + +static av_cold void avisynth_atexit_handler(void) { + AviSynthContext *avs = avs_ctx_list; + + while (avs) { + AviSynthContext *next = avs->next; + avisynth_context_destroy(avs); + avs = next; + } + FreeLibrary(avs_library->library); + av_freep(&avs_library); + + avs_atexit_called = 1; +} + +// Create AVStream from audio and video data. +static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) { + AviSynthContext *avs = s->priv_data; + int planar = 0; // 0: packed, 1: YUV, 2: Y8 + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = CODEC_ID_RAWVIDEO; + st->codec->width = avs->vi->width; + st->codec->height = avs->vi->height; + + st->time_base = (AVRational) {avs->vi->fps_denominator, avs->vi->fps_numerator}; + st->avg_frame_rate = (AVRational) {avs->vi->fps_numerator, avs->vi->fps_denominator}; + st->start_time = 0; + st->duration = avs->vi->num_frames; + st->nb_frames = avs->vi->num_frames; + + switch (avs->vi->pixel_type) { +#ifdef _WIN32 + case AVS_CS_YV24: + st->codec->pix_fmt = PIX_FMT_YUV444P; + planar = 1; + break; + case AVS_CS_YV16: + st->codec->pix_fmt = PIX_FMT_YUV422P; + planar = 1; + break; + case AVS_CS_YV411: + st->codec->pix_fmt = PIX_FMT_YUV411P; + planar = 1; + break; + case AVS_CS_Y8: + st->codec->pix_fmt = PIX_FMT_GRAY8; + planar = 2; + break; +#endif + case AVS_CS_BGR24: + st->codec->pix_fmt = PIX_FMT_BGR24; + break; + case AVS_CS_BGR32: + st->codec->pix_fmt = PIX_FMT_RGB32; + break; + case AVS_CS_YUY2: + st->codec->pix_fmt = PIX_FMT_YUYV422; + break; + case AVS_CS_YV12: + st->codec->pix_fmt = PIX_FMT_YUV420P; + planar = 1; + break; + case AVS_CS_I420: // Is this even used anywhere? + st->codec->pix_fmt = PIX_FMT_YUV420P; + planar = 1; + break; + default: + av_log(s, AV_LOG_ERROR, "unknown AviSynth colorspace %d\n", avs->vi->pixel_type); + avs->error = 1; + return AVERROR_UNKNOWN; + } + + switch (planar) { + case 2: // Y8 + avs->n_planes = 1; + avs->planes = avs_planes_grey; + break; + case 1: // YUV + avs->n_planes = 3; + avs->planes = avs_planes_yuv; + break; + default: + avs->n_planes = 1; + avs->planes = avs_planes_packed; + } + return 0; +} + +static int avisynth_create_stream_audio(AVFormatContext *s, AVStream *st) { + AviSynthContext *avs = s->priv_data; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->sample_rate = avs->vi->audio_samples_per_second; + st->codec->channels = avs->vi->nchannels; + st->time_base = (AVRational) {1, avs->vi->audio_samples_per_second}; + + switch (avs->vi->sample_type) { + case AVS_SAMPLE_INT8: + st->codec->codec_id = CODEC_ID_PCM_U8; + break; + case AVS_SAMPLE_INT16: + st->codec->codec_id = CODEC_ID_PCM_S16LE; + break; + case AVS_SAMPLE_INT24: + st->codec->codec_id = CODEC_ID_PCM_S24LE; + break; + case AVS_SAMPLE_INT32: + st->codec->codec_id = CODEC_ID_PCM_S32LE; + break; + case AVS_SAMPLE_FLOAT: + st->codec->codec_id = CODEC_ID_PCM_F32LE; + break; + default: + av_log(s, AV_LOG_ERROR, "unknown AviSynth sample type %d\n", avs->vi->sample_type); + avs->error = 1; + return AVERROR_UNKNOWN; + } + return 0; +} + +static int avisynth_create_stream(AVFormatContext *s) { + AviSynthContext *avs = s->priv_data; + AVStream *st; + int ret; + int id = 0; + + if (avs_has_video(avs->vi)) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR_UNKNOWN; + st->id = id++; + if (ret = avisynth_create_stream_video(s, st)) + return ret; + } + if (avs_has_audio(avs->vi)) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR_UNKNOWN; + st->id = id++; + if (ret = avisynth_create_stream_audio(s, st)) + return ret; + } + return 0; +} + +static int avisynth_open_file(AVFormatContext *s) { + AviSynthContext *avs = (AviSynthContext *)s->priv_data; + AVS_Value arg, val; + int ret; + + if (ret = avisynth_context_create(s)) + return ret; + + arg = avs_new_value_string(s->filename); + val = avs_library->avs_invoke(avs->env, "Import", arg, 0); + if (avs_is_error(val)) { + av_log(s, AV_LOG_ERROR, "%s\n", avs_as_error(val)); + ret = AVERROR_UNKNOWN; + goto fail; + } + if (!avs_is_clip(val)) { + av_log(s, AV_LOG_ERROR, "%s\n", "AviSynth script did not return a clip"); + ret = AVERROR_UNKNOWN; + goto fail; + } + + avs->clip = avs_library->avs_take_clip(val, avs->env); + avs->vi = avs_library->avs_get_video_info(avs->clip); + + // Release the AVS_Value as it will go out of scope. + avs_library->avs_release_value(val); + + if (ret = avisynth_create_stream(s)) + goto fail; + + return 0; + +fail: + avisynth_context_destroy(avs); + return ret; +} + +static void avisynth_next_stream(AVFormatContext *s, AVStream **st, AVPacket *pkt, int *discard) { + AviSynthContext *avs = s->priv_data; + + pkt->stream_index = avs->curr_stream++; + avs->curr_stream %= s->nb_streams; + + *st = s->streams[pkt->stream_index]; + if ((*st)->discard == AVDISCARD_ALL) + *discard = 1; + else + *discard = 0; + + return; +} + +// Copy AviSynth clip data into an AVPacket. +static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, int discard) { + AviSynthContext *avs = s->priv_data; + AVS_VideoFrame *frame; + unsigned char *dst_p; + const unsigned char *src_p; + int n, i, plane, rowsize, planeheight, pitch, bits; + const char *error; + + if (avs->curr_frame >= avs->vi->num_frames) + return AVERROR_EOF; + + // This must happen even if the stream is discarded to prevent desync. + n = avs->curr_frame++; + if (discard) + return 0; + + pkt->pts = n; + pkt->dts = n; + pkt->duration = 1; + + // Define the bpp values for the new AviSynth 2.6 colorspaces + if (avs_is_yv24(avs->vi)) { + bits = 24; + } else if (avs_is_yv16(avs->vi)) { + bits = 16; + } else if (avs_is_yv411(avs->vi)) { + bits = 12; + } else if (avs_is_y8(avs->vi)) { + bits = 8; + } else { + bits = avs_bits_per_pixel(avs->vi); + } + + // Without cast to int64_t, calculation overflows at about 9k x 9k resolution. + pkt->size = (((int64_t)avs->vi->width * (int64_t)avs->vi->height) * bits) / 8; + if (!pkt->size) + return AVERROR_UNKNOWN; + pkt->data = av_malloc(pkt->size); + if (!pkt->data) + return AVERROR_UNKNOWN; + + frame = avs_library->avs_get_frame(avs->clip, n); + error = avs_library->avs_clip_get_error(avs->clip); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + avs->error = 1; + av_freep(&pkt->data); + return AVERROR_UNKNOWN; + } + + dst_p = pkt->data; + for (i = 0; i < avs->n_planes; i++) { + plane = avs->planes[i]; + src_p = avs_get_read_ptr_p(frame, plane); + rowsize = avs_get_row_size_p(frame, plane); + planeheight = avs_get_height_p(frame, plane); + pitch = avs_get_pitch_p(frame, plane); + + // Flip RGB video. + if (avs_is_rgb24(avs->vi) || avs_is_rgb(avs->vi)) { + src_p = src_p + (planeheight - 1) * pitch; + pitch = -pitch; + } + + avs_library->avs_bit_blt(avs->env, dst_p, rowsize, src_p, pitch, rowsize, planeheight); + dst_p += rowsize * planeheight; + } + + avs_library->avs_release_video_frame(frame); + return 0; +} + +static int avisynth_read_packet_audio(AVFormatContext *s, AVPacket *pkt, int discard) { + AviSynthContext *avs = s->priv_data; + AVRational fps, samplerate; + int samples; + int64_t n; + const char *error; + + if (avs->curr_sample >= avs->vi->num_audio_samples) + return AVERROR_EOF; + + fps.num = avs->vi->fps_numerator; + fps.den = avs->vi->fps_denominator; + samplerate.num = avs->vi->audio_samples_per_second; + samplerate.den = 1; + + if (avs_has_video(avs->vi)) { + if (avs->curr_frame < avs->vi->num_frames) + samples = av_rescale_q(avs->curr_frame, samplerate, fps) - avs->curr_sample; + else + samples = av_rescale_q(1, samplerate, fps); + } else { + samples = 1000; + } + + // After seeking, audio may catch up with video. + if (samples <= 0) { + pkt->size = 0; + pkt->data = NULL; + return 0; + } + + if (avs->curr_sample + samples > avs->vi->num_audio_samples) + samples = avs->vi->num_audio_samples - avs->curr_sample; + + // This must happen even if the stream is discarded to prevent desync. + n = avs->curr_sample; + avs->curr_sample += samples; + if (discard) + return 0; + + pkt->pts = n; + pkt->dts = n; + pkt->duration = samples; + + pkt->size = avs_bytes_per_channel_sample(avs->vi) * samples * avs->vi->nchannels; + if (!pkt->size) + return AVERROR_UNKNOWN; + pkt->data = av_malloc(pkt->size); + if (!pkt->data) + return AVERROR_UNKNOWN; + + avs_library->avs_get_audio(avs->clip, pkt->data, n, samples); + error = avs_library->avs_clip_get_error(avs->clip); + if (error) { + av_log(s, AV_LOG_ERROR, "%s\n", error); + avs->error = 1; + av_freep(&pkt->data); + return AVERROR_UNKNOWN; + } + return 0; +} + +static av_cold int avisynth_read_header(AVFormatContext *s) { + int ret; + + // Calling library must implement a lock for thread-safe opens. + if (ret = avpriv_lock_avformat()) + return ret; + + if (ret = avisynth_open_file(s)) { + avpriv_unlock_avformat(); + return ret; + } + + avpriv_unlock_avformat(); + return 0; +} + +static int avisynth_read_packet(AVFormatContext *s, AVPacket *pkt) { + AviSynthContext *avs = s->priv_data; + AVStream *st; + int discard = 0; + int ret; + + if (avs->error) + return AVERROR_UNKNOWN; + + pkt->destruct = av_destruct_packet; + + // If either stream reaches EOF, try to read the other one before giving up. + avisynth_next_stream(s, &st, pkt, &discard); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = avisynth_read_packet_video(s, pkt, discard); + if (ret == AVERROR_EOF && avs_has_audio(avs->vi)) { + avisynth_next_stream(s, &st, pkt, &discard); + return avisynth_read_packet_audio(s, pkt, discard); + } + return ret; + } else { + ret = avisynth_read_packet_audio(s, pkt, discard); + if (ret == AVERROR_EOF && avs_has_video(avs->vi)) { + avisynth_next_stream(s, &st, pkt, &discard); + return avisynth_read_packet_video(s, pkt, discard); + } + return ret; + } +} + +static av_cold int avisynth_read_close(AVFormatContext *s) { + if (avpriv_lock_avformat()) + return AVERROR_UNKNOWN; + + avisynth_context_destroy(s->priv_data); + avpriv_unlock_avformat(); + return 0; +} + +static int avisynth_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { + AviSynthContext *avs = s->priv_data; + AVStream *st; + AVRational fps, samplerate; + + if (avs->error) + return AVERROR_UNKNOWN; + + fps = (AVRational) {avs->vi->fps_numerator, avs->vi->fps_denominator}; + samplerate = (AVRational) {avs->vi->audio_samples_per_second, 1}; + + st = s->streams[stream_index]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + // AviSynth frame counts are signed int. + if ((timestamp >= avs->vi->num_frames) || (timestamp > INT_MAX) || (timestamp < 0)) + return AVERROR_EOF; + avs->curr_frame = timestamp; + if (avs_has_audio(avs->vi)) + avs->curr_sample = av_rescale_q(timestamp, samplerate, fps); + } else { + if ((timestamp >= avs->vi->num_audio_samples) || (timestamp < 0)) + return AVERROR_EOF; + // Force frame granularity for seeking. + if (avs_has_video(avs->vi)) { + avs->curr_frame = av_rescale_q(timestamp, fps, samplerate); + avs->curr_sample = av_rescale_q(avs->curr_frame, samplerate, fps); + } else { + avs->curr_sample = timestamp; + } + } + + return 0; +} + +AVInputFormat ff_avisynth_demuxer = { + .name = "avisynth", + .long_name = NULL_IF_CONFIG_SMALL("AviSynth script"), + .priv_data_size = sizeof(AviSynthContext), + .read_header = avisynth_read_header, + .read_packet = avisynth_read_packet, + .read_close = avisynth_read_close, + .read_seek = avisynth_read_seek, + .extensions = "avs", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avlanguage.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avlanguage.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,765 @@ +/* + * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avlanguage.h" +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include +#include +#include + +typedef struct LangEntry { + const char str[4]; + uint16_t next_equivalent; +} LangEntry; + +static const uint16_t lang_table_counts[] = { 484, 20, 184 }; +static const uint16_t lang_table_offsets[] = { 0, 484, 504 }; + +static const LangEntry lang_table[] = { + /*----- AV_LANG_ISO639_2_BIBL entries (484) -----*/ + /*0000*/ { "aar", 504 }, + /*0001*/ { "abk", 505 }, + /*0002*/ { "ace", 2 }, + /*0003*/ { "ach", 3 }, + /*0004*/ { "ada", 4 }, + /*0005*/ { "ady", 5 }, + /*0006*/ { "afa", 6 }, + /*0007*/ { "afh", 7 }, + /*0008*/ { "afr", 507 }, + /*0009*/ { "ain", 9 }, + /*0010*/ { "aka", 508 }, + /*0011*/ { "akk", 11 }, + /*0012*/ { "alb", 502 }, + /*0013*/ { "ale", 13 }, + /*0014*/ { "alg", 14 }, + /*0015*/ { "alt", 15 }, + /*0016*/ { "amh", 509 }, + /*0017*/ { "ang", 17 }, + /*0018*/ { "anp", 18 }, + /*0019*/ { "apa", 19 }, + /*0020*/ { "ara", 511 }, + /*0021*/ { "arc", 21 }, + /*0022*/ { "arg", 510 }, + /*0023*/ { "arm", 492 }, + /*0024*/ { "arn", 24 }, + /*0025*/ { "arp", 25 }, + /*0026*/ { "art", 26 }, + /*0027*/ { "arw", 27 }, + /*0028*/ { "asm", 512 }, + /*0029*/ { "ast", 29 }, + /*0030*/ { "ath", 30 }, + /*0031*/ { "aus", 31 }, + /*0032*/ { "ava", 513 }, + /*0033*/ { "ave", 506 }, + /*0034*/ { "awa", 34 }, + /*0035*/ { "aym", 514 }, + /*0036*/ { "aze", 515 }, + /*0037*/ { "bad", 37 }, + /*0038*/ { "bai", 38 }, + /*0039*/ { "bak", 516 }, + /*0040*/ { "bal", 40 }, + /*0041*/ { "bam", 521 }, + /*0042*/ { "ban", 42 }, + /*0043*/ { "baq", 489 }, + /*0044*/ { "bas", 44 }, + /*0045*/ { "bat", 45 }, + /*0046*/ { "bej", 46 }, + /*0047*/ { "bel", 517 }, + /*0048*/ { "bem", 48 }, + /*0049*/ { "ben", 522 }, + /*0050*/ { "ber", 50 }, + /*0051*/ { "bho", 51 }, + /*0052*/ { "bih", 519 }, + /*0053*/ { "bik", 53 }, + /*0054*/ { "bin", 54 }, + /*0055*/ { "bis", 520 }, + /*0056*/ { "bla", 56 }, + /*0057*/ { "bnt", 57 }, + /*0058*/ { "bos", 525 }, + /*0059*/ { "bra", 59 }, + /*0060*/ { "bre", 524 }, + /*0061*/ { "btk", 61 }, + /*0062*/ { "bua", 62 }, + /*0063*/ { "bug", 63 }, + /*0064*/ { "bul", 518 }, + /*0065*/ { "bur", 498 }, + /*0066*/ { "byn", 66 }, + /*0067*/ { "cad", 67 }, + /*0068*/ { "cai", 68 }, + /*0069*/ { "car", 69 }, + /*0070*/ { "cat", 526 }, + /*0071*/ { "cau", 71 }, + /*0072*/ { "ceb", 72 }, + /*0073*/ { "cel", 73 }, + /*0074*/ { "cha", 528 }, + /*0075*/ { "chb", 75 }, + /*0076*/ { "che", 527 }, + /*0077*/ { "chg", 77 }, + /*0078*/ { "chi", 503 }, + /*0079*/ { "chk", 79 }, + /*0080*/ { "chm", 80 }, + /*0081*/ { "chn", 81 }, + /*0082*/ { "cho", 82 }, + /*0083*/ { "chp", 83 }, + /*0084*/ { "chr", 84 }, + /*0085*/ { "chu", 532 }, + /*0086*/ { "chv", 533 }, + /*0087*/ { "chy", 87 }, + /*0088*/ { "cmc", 88 }, + /*0089*/ { "cop", 89 }, + /*0090*/ { "cor", 593 }, + /*0091*/ { "cos", 529 }, + /*0092*/ { "cpe", 92 }, + /*0093*/ { "cpf", 93 }, + /*0094*/ { "cpp", 94 }, + /*0095*/ { "cre", 530 }, + /*0096*/ { "crh", 96 }, + /*0097*/ { "crp", 97 }, + /*0098*/ { "csb", 98 }, + /*0099*/ { "cus", 99 }, + /*0100*/ { "cze", 485 }, + /*0101*/ { "dak", 101 }, + /*0102*/ { "dan", 535 }, + /*0103*/ { "dar", 103 }, + /*0104*/ { "day", 104 }, + /*0105*/ { "del", 105 }, + /*0106*/ { "den", 106 }, + /*0107*/ { "dgr", 107 }, + /*0108*/ { "din", 108 }, + /*0109*/ { "div", 537 }, + /*0110*/ { "doi", 110 }, + /*0111*/ { "dra", 111 }, + /*0112*/ { "dsb", 112 }, + /*0113*/ { "dua", 113 }, + /*0114*/ { "dum", 114 }, + /*0115*/ { "dut", 499 }, + /*0116*/ { "dyu", 116 }, + /*0117*/ { "dzo", 538 }, + /*0118*/ { "efi", 118 }, + /*0119*/ { "egy", 119 }, + /*0120*/ { "eka", 120 }, + /*0121*/ { "elx", 121 }, + /*0122*/ { "eng", 541 }, + /*0123*/ { "enm", 123 }, + /*0124*/ { "epo", 542 }, + /*0125*/ { "est", 544 }, + /*0126*/ { "ewe", 539 }, + /*0127*/ { "ewo", 127 }, + /*0128*/ { "fan", 128 }, + /*0129*/ { "fao", 550 }, + /*0130*/ { "fat", 130 }, + /*0131*/ { "fij", 549 }, + /*0132*/ { "fil", 132 }, + /*0133*/ { "fin", 548 }, + /*0134*/ { "fiu", 134 }, + /*0135*/ { "fon", 135 }, + /*0136*/ { "fre", 491 }, + /*0137*/ { "frm", 137 }, + /*0138*/ { "fro", 138 }, + /*0139*/ { "frr", 139 }, + /*0140*/ { "frs", 140 }, + /*0141*/ { "fry", 552 }, + /*0142*/ { "ful", 547 }, + /*0143*/ { "fur", 143 }, + /*0144*/ { "gaa", 144 }, + /*0145*/ { "gay", 145 }, + /*0146*/ { "gba", 146 }, + /*0147*/ { "gem", 147 }, + /*0148*/ { "geo", 494 }, + /*0149*/ { "ger", 487 }, + /*0150*/ { "gez", 150 }, + /*0151*/ { "gil", 151 }, + /*0152*/ { "gla", 554 }, + /*0153*/ { "gle", 553 }, + /*0154*/ { "glg", 555 }, + /*0155*/ { "glv", 558 }, + /*0156*/ { "gmh", 156 }, + /*0157*/ { "goh", 157 }, + /*0158*/ { "gon", 158 }, + /*0159*/ { "gor", 159 }, + /*0160*/ { "got", 160 }, + /*0161*/ { "grb", 161 }, + /*0162*/ { "grc", 162 }, + /*0163*/ { "gre", 488 }, + /*0164*/ { "grn", 556 }, + /*0165*/ { "gsw", 165 }, + /*0166*/ { "guj", 557 }, + /*0167*/ { "gwi", 167 }, + /*0168*/ { "hai", 168 }, + /*0169*/ { "hat", 564 }, + /*0170*/ { "hau", 559 }, + /*0171*/ { "haw", 171 }, + /*0172*/ { "heb", 560 }, + /*0173*/ { "her", 567 }, + /*0174*/ { "hil", 174 }, + /*0175*/ { "him", 175 }, + /*0176*/ { "hin", 561 }, + /*0177*/ { "hit", 177 }, + /*0178*/ { "hmn", 178 }, + /*0179*/ { "hmo", 562 }, + /*0180*/ { "hrv", 563 }, + /*0181*/ { "hsb", 181 }, + /*0182*/ { "hun", 565 }, + /*0183*/ { "hup", 183 }, + /*0184*/ { "iba", 184 }, + /*0185*/ { "ibo", 571 }, + /*0186*/ { "ice", 493 }, + /*0187*/ { "ido", 574 }, + /*0188*/ { "iii", 572 }, + /*0189*/ { "ijo", 189 }, + /*0190*/ { "iku", 577 }, + /*0191*/ { "ile", 570 }, + /*0192*/ { "ilo", 192 }, + /*0193*/ { "ina", 568 }, + /*0194*/ { "inc", 194 }, + /*0195*/ { "ind", 569 }, + /*0196*/ { "ine", 196 }, + /*0197*/ { "inh", 197 }, + /*0198*/ { "ipk", 573 }, + /*0199*/ { "ira", 199 }, + /*0200*/ { "iro", 200 }, + /*0201*/ { "ita", 576 }, + /*0202*/ { "jav", 579 }, + /*0203*/ { "jbo", 203 }, + /*0204*/ { "jpn", 578 }, + /*0205*/ { "jpr", 205 }, + /*0206*/ { "jrb", 206 }, + /*0207*/ { "kaa", 207 }, + /*0208*/ { "kab", 208 }, + /*0209*/ { "kac", 209 }, + /*0210*/ { "kal", 585 }, + /*0211*/ { "kam", 211 }, + /*0212*/ { "kan", 587 }, + /*0213*/ { "kar", 213 }, + /*0214*/ { "kas", 590 }, + /*0215*/ { "kau", 589 }, + /*0216*/ { "kaw", 216 }, + /*0217*/ { "kaz", 584 }, + /*0218*/ { "kbd", 218 }, + /*0219*/ { "kha", 219 }, + /*0220*/ { "khi", 220 }, + /*0221*/ { "khm", 586 }, + /*0222*/ { "kho", 222 }, + /*0223*/ { "kik", 582 }, + /*0224*/ { "kin", 640 }, + /*0225*/ { "kir", 594 }, + /*0226*/ { "kmb", 226 }, + /*0227*/ { "kok", 227 }, + /*0228*/ { "kom", 592 }, + /*0229*/ { "kon", 581 }, + /*0230*/ { "kor", 588 }, + /*0231*/ { "kos", 231 }, + /*0232*/ { "kpe", 232 }, + /*0233*/ { "krc", 233 }, + /*0234*/ { "krl", 234 }, + /*0235*/ { "kro", 235 }, + /*0236*/ { "kru", 236 }, + /*0237*/ { "kua", 583 }, + /*0238*/ { "kum", 238 }, + /*0239*/ { "kur", 591 }, + /*0240*/ { "kut", 240 }, + /*0241*/ { "lad", 241 }, + /*0242*/ { "lah", 242 }, + /*0243*/ { "lam", 243 }, + /*0244*/ { "lao", 600 }, + /*0245*/ { "lat", 595 }, + /*0246*/ { "lav", 603 }, + /*0247*/ { "lez", 247 }, + /*0248*/ { "lim", 598 }, + /*0249*/ { "lin", 599 }, + /*0250*/ { "lit", 601 }, + /*0251*/ { "lol", 251 }, + /*0252*/ { "loz", 252 }, + /*0253*/ { "ltz", 596 }, + /*0254*/ { "lua", 254 }, + /*0255*/ { "lub", 602 }, + /*0256*/ { "lug", 597 }, + /*0257*/ { "lui", 257 }, + /*0258*/ { "lun", 258 }, + /*0259*/ { "luo", 259 }, + /*0260*/ { "lus", 260 }, + /*0261*/ { "mac", 495 }, + /*0262*/ { "mad", 262 }, + /*0263*/ { "mag", 263 }, + /*0264*/ { "mah", 605 }, + /*0265*/ { "mai", 265 }, + /*0266*/ { "mak", 266 }, + /*0267*/ { "mal", 608 }, + /*0268*/ { "man", 268 }, + /*0269*/ { "mao", 496 }, + /*0270*/ { "map", 270 }, + /*0271*/ { "mar", 610 }, + /*0272*/ { "mas", 272 }, + /*0273*/ { "may", 497 }, + /*0274*/ { "mdf", 274 }, + /*0275*/ { "mdr", 275 }, + /*0276*/ { "men", 276 }, + /*0277*/ { "mga", 277 }, + /*0278*/ { "mic", 278 }, + /*0279*/ { "min", 279 }, + /*0280*/ { "mis", 280 }, + /*0281*/ { "mkh", 281 }, + /*0282*/ { "mlg", 604 }, + /*0283*/ { "mlt", 612 }, + /*0284*/ { "mnc", 284 }, + /*0285*/ { "mni", 285 }, + /*0286*/ { "mno", 286 }, + /*0287*/ { "moh", 287 }, + /*0288*/ { "mon", 609 }, + /*0289*/ { "mos", 289 }, + /*0290*/ { "mul", 290 }, + /*0291*/ { "mun", 291 }, + /*0292*/ { "mus", 292 }, + /*0293*/ { "mwl", 293 }, + /*0294*/ { "mwr", 294 }, + /*0295*/ { "myn", 295 }, + /*0296*/ { "myv", 296 }, + /*0297*/ { "nah", 297 }, + /*0298*/ { "nai", 298 }, + /*0299*/ { "nap", 299 }, + /*0300*/ { "nau", 614 }, + /*0301*/ { "nav", 623 }, + /*0302*/ { "nbl", 622 }, + /*0303*/ { "nde", 616 }, + /*0304*/ { "ndo", 618 }, + /*0305*/ { "nds", 305 }, + /*0306*/ { "nep", 617 }, + /*0307*/ { "new", 307 }, + /*0308*/ { "nia", 308 }, + /*0309*/ { "nic", 309 }, + /*0310*/ { "niu", 310 }, + /*0311*/ { "nno", 620 }, + /*0312*/ { "nob", 615 }, + /*0313*/ { "nog", 313 }, + /*0314*/ { "non", 314 }, + /*0315*/ { "nor", 621 }, + /*0316*/ { "nqo", 316 }, + /*0317*/ { "nso", 317 }, + /*0318*/ { "nub", 318 }, + /*0319*/ { "nwc", 319 }, + /*0320*/ { "nya", 624 }, + /*0321*/ { "nym", 321 }, + /*0322*/ { "nyn", 322 }, + /*0323*/ { "nyo", 323 }, + /*0324*/ { "nzi", 324 }, + /*0325*/ { "oci", 625 }, + /*0326*/ { "oji", 626 }, + /*0327*/ { "ori", 628 }, + /*0328*/ { "orm", 627 }, + /*0329*/ { "osa", 329 }, + /*0330*/ { "oss", 629 }, + /*0331*/ { "ota", 331 }, + /*0332*/ { "oto", 332 }, + /*0333*/ { "paa", 333 }, + /*0334*/ { "pag", 334 }, + /*0335*/ { "pal", 335 }, + /*0336*/ { "pam", 336 }, + /*0337*/ { "pan", 630 }, + /*0338*/ { "pap", 338 }, + /*0339*/ { "pau", 339 }, + /*0340*/ { "peo", 340 }, + /*0341*/ { "per", 490 }, + /*0342*/ { "phi", 342 }, + /*0343*/ { "phn", 343 }, + /*0344*/ { "pli", 631 }, + /*0345*/ { "pol", 632 }, + /*0346*/ { "pon", 346 }, + /*0347*/ { "por", 634 }, + /*0348*/ { "pra", 348 }, + /*0349*/ { "pro", 349 }, + /*0350*/ { "pus", 633 }, + /*0351*/ { "que", 635 }, + /*0352*/ { "raj", 352 }, + /*0353*/ { "rap", 353 }, + /*0354*/ { "rar", 354 }, + /*0355*/ { "roa", 355 }, + /*0356*/ { "roh", 636 }, + /*0357*/ { "rom", 357 }, + /*0358*/ { "rum", 500 }, + /*0359*/ { "run", 637 }, + /*0360*/ { "rup", 360 }, + /*0361*/ { "rus", 639 }, + /*0362*/ { "sad", 362 }, + /*0363*/ { "sag", 645 }, + /*0364*/ { "sah", 364 }, + /*0365*/ { "sai", 365 }, + /*0366*/ { "sal", 366 }, + /*0367*/ { "sam", 367 }, + /*0368*/ { "san", 641 }, + /*0369*/ { "sas", 369 }, + /*0370*/ { "sat", 370 }, + /*0371*/ { "scn", 371 }, + /*0372*/ { "sco", 372 }, + /*0373*/ { "sel", 373 }, + /*0374*/ { "sem", 374 }, + /*0375*/ { "sga", 375 }, + /*0376*/ { "sgn", 376 }, + /*0377*/ { "shn", 377 }, + /*0378*/ { "sid", 378 }, + /*0379*/ { "sin", 646 }, + /*0380*/ { "sio", 380 }, + /*0381*/ { "sit", 381 }, + /*0382*/ { "sla", 382 }, + /*0383*/ { "slo", 501 }, + /*0384*/ { "slv", 648 }, + /*0385*/ { "sma", 385 }, + /*0386*/ { "sme", 644 }, + /*0387*/ { "smi", 387 }, + /*0388*/ { "smj", 388 }, + /*0389*/ { "smn", 389 }, + /*0390*/ { "smo", 649 }, + /*0391*/ { "sms", 391 }, + /*0392*/ { "sna", 650 }, + /*0393*/ { "snd", 643 }, + /*0394*/ { "snk", 394 }, + /*0395*/ { "sog", 395 }, + /*0396*/ { "som", 651 }, + /*0397*/ { "son", 397 }, + /*0398*/ { "sot", 655 }, + /*0399*/ { "spa", 543 }, + /*0400*/ { "srd", 642 }, + /*0401*/ { "srn", 401 }, + /*0402*/ { "srp", 653 }, + /*0403*/ { "srr", 403 }, + /*0404*/ { "ssa", 404 }, + /*0405*/ { "ssw", 654 }, + /*0406*/ { "suk", 406 }, + /*0407*/ { "sun", 656 }, + /*0408*/ { "sus", 408 }, + /*0409*/ { "sux", 409 }, + /*0410*/ { "swa", 658 }, + /*0411*/ { "swe", 657 }, + /*0412*/ { "syc", 412 }, + /*0413*/ { "syr", 413 }, + /*0414*/ { "tah", 672 }, + /*0415*/ { "tai", 415 }, + /*0416*/ { "tam", 659 }, + /*0417*/ { "tat", 670 }, + /*0418*/ { "tel", 660 }, + /*0419*/ { "tem", 419 }, + /*0420*/ { "ter", 420 }, + /*0421*/ { "tet", 421 }, + /*0422*/ { "tgk", 661 }, + /*0423*/ { "tgl", 665 }, + /*0424*/ { "tha", 662 }, + /*0425*/ { "tib", 484 }, + /*0426*/ { "tig", 426 }, + /*0427*/ { "tir", 663 }, + /*0428*/ { "tiv", 428 }, + /*0429*/ { "tkl", 429 }, + /*0430*/ { "tlh", 430 }, + /*0431*/ { "tli", 431 }, + /*0432*/ { "tmh", 432 }, + /*0433*/ { "tog", 433 }, + /*0434*/ { "ton", 667 }, + /*0435*/ { "tpi", 435 }, + /*0436*/ { "tsi", 436 }, + /*0437*/ { "tsn", 666 }, + /*0438*/ { "tso", 669 }, + /*0439*/ { "tuk", 664 }, + /*0440*/ { "tum", 440 }, + /*0441*/ { "tup", 441 }, + /*0442*/ { "tur", 668 }, + /*0443*/ { "tut", 443 }, + /*0444*/ { "tvl", 444 }, + /*0445*/ { "twi", 671 }, + /*0446*/ { "tyv", 446 }, + /*0447*/ { "udm", 447 }, + /*0448*/ { "uga", 448 }, + /*0449*/ { "uig", 673 }, + /*0450*/ { "ukr", 674 }, + /*0451*/ { "umb", 451 }, + /*0452*/ { "und", 452 }, + /*0453*/ { "urd", 675 }, + /*0454*/ { "uzb", 676 }, + /*0455*/ { "vai", 455 }, + /*0456*/ { "ven", 677 }, + /*0457*/ { "vie", 678 }, + /*0458*/ { "vol", 679 }, + /*0459*/ { "vot", 459 }, + /*0460*/ { "wak", 460 }, + /*0461*/ { "wal", 461 }, + /*0462*/ { "war", 462 }, + /*0463*/ { "was", 463 }, + /*0464*/ { "wel", 486 }, + /*0465*/ { "wen", 465 }, + /*0466*/ { "wln", 680 }, + /*0467*/ { "wol", 681 }, + /*0468*/ { "xal", 468 }, + /*0469*/ { "xho", 682 }, + /*0470*/ { "yao", 470 }, + /*0471*/ { "yap", 471 }, + /*0472*/ { "yid", 683 }, + /*0473*/ { "yor", 684 }, + /*0474*/ { "ypk", 474 }, + /*0475*/ { "zap", 475 }, + /*0476*/ { "zbl", 476 }, + /*0477*/ { "zen", 477 }, + /*0478*/ { "zha", 685 }, + /*0479*/ { "znd", 479 }, + /*0480*/ { "zul", 687 }, + /*0481*/ { "zun", 481 }, + /*0482*/ { "zxx", 482 }, + /*0483*/ { "zza", 483 }, + /*----- AV_LANG_ISO639_2_TERM entries (20) -----*/ + /*0484*/ { "bod", 523 }, + /*0485*/ { "ces", 531 }, + /*0486*/ { "cym", 534 }, + /*0487*/ { "deu", 536 }, + /*0488*/ { "ell", 540 }, + /*0489*/ { "eus", 545 }, + /*0490*/ { "fas", 546 }, + /*0491*/ { "fra", 551 }, + /*0492*/ { "hye", 566 }, + /*0493*/ { "isl", 575 }, + /*0494*/ { "kat", 580 }, + /*0495*/ { "mkd", 607 }, + /*0496*/ { "mri", 606 }, + /*0497*/ { "msa", 611 }, + /*0498*/ { "mya", 613 }, + /*0499*/ { "nld", 619 }, + /*0500*/ { "ron", 638 }, + /*0501*/ { "slk", 647 }, + /*0502*/ { "sqi", 652 }, + /*0503*/ { "zho", 686 }, + /*----- AV_LANG_ISO639_1 entries (184) -----*/ + /*0504*/ { "aa" , 0 }, + /*0505*/ { "ab" , 1 }, + /*0506*/ { "ae" , 33 }, + /*0507*/ { "af" , 8 }, + /*0508*/ { "ak" , 10 }, + /*0509*/ { "am" , 16 }, + /*0510*/ { "an" , 22 }, + /*0511*/ { "ar" , 20 }, + /*0512*/ { "as" , 28 }, + /*0513*/ { "av" , 32 }, + /*0514*/ { "ay" , 35 }, + /*0515*/ { "az" , 36 }, + /*0516*/ { "ba" , 39 }, + /*0517*/ { "be" , 47 }, + /*0518*/ { "bg" , 64 }, + /*0519*/ { "bh" , 52 }, + /*0520*/ { "bi" , 55 }, + /*0521*/ { "bm" , 41 }, + /*0522*/ { "bn" , 49 }, + /*0523*/ { "bo" , 425 }, + /*0524*/ { "br" , 60 }, + /*0525*/ { "bs" , 58 }, + /*0526*/ { "ca" , 70 }, + /*0527*/ { "ce" , 76 }, + /*0528*/ { "ch" , 74 }, + /*0529*/ { "co" , 91 }, + /*0530*/ { "cr" , 95 }, + /*0531*/ { "cs" , 100 }, + /*0532*/ { "cu" , 85 }, + /*0533*/ { "cv" , 86 }, + /*0534*/ { "cy" , 464 }, + /*0535*/ { "da" , 102 }, + /*0536*/ { "de" , 149 }, + /*0537*/ { "dv" , 109 }, + /*0538*/ { "dz" , 117 }, + /*0539*/ { "ee" , 126 }, + /*0540*/ { "el" , 163 }, + /*0541*/ { "en" , 122 }, + /*0542*/ { "eo" , 124 }, + /*0543*/ { "es" , 399 }, + /*0544*/ { "et" , 125 }, + /*0545*/ { "eu" , 43 }, + /*0546*/ { "fa" , 341 }, + /*0547*/ { "ff" , 142 }, + /*0548*/ { "fi" , 133 }, + /*0549*/ { "fj" , 131 }, + /*0550*/ { "fo" , 129 }, + /*0551*/ { "fr" , 136 }, + /*0552*/ { "fy" , 141 }, + /*0553*/ { "ga" , 153 }, + /*0554*/ { "gd" , 152 }, + /*0555*/ { "gl" , 154 }, + /*0556*/ { "gn" , 164 }, + /*0557*/ { "gu" , 166 }, + /*0558*/ { "gv" , 155 }, + /*0559*/ { "ha" , 170 }, + /*0560*/ { "he" , 172 }, + /*0561*/ { "hi" , 176 }, + /*0562*/ { "ho" , 179 }, + /*0563*/ { "hr" , 180 }, + /*0564*/ { "ht" , 169 }, + /*0565*/ { "hu" , 182 }, + /*0566*/ { "hy" , 23 }, + /*0567*/ { "hz" , 173 }, + /*0568*/ { "ia" , 193 }, + /*0569*/ { "id" , 195 }, + /*0570*/ { "ie" , 191 }, + /*0571*/ { "ig" , 185 }, + /*0572*/ { "ii" , 188 }, + /*0573*/ { "ik" , 198 }, + /*0574*/ { "io" , 187 }, + /*0575*/ { "is" , 186 }, + /*0576*/ { "it" , 201 }, + /*0577*/ { "iu" , 190 }, + /*0578*/ { "ja" , 204 }, + /*0579*/ { "jv" , 202 }, + /*0580*/ { "ka" , 148 }, + /*0581*/ { "kg" , 229 }, + /*0582*/ { "ki" , 223 }, + /*0583*/ { "kj" , 237 }, + /*0584*/ { "kk" , 217 }, + /*0585*/ { "kl" , 210 }, + /*0586*/ { "km" , 221 }, + /*0587*/ { "kn" , 212 }, + /*0588*/ { "ko" , 230 }, + /*0589*/ { "kr" , 215 }, + /*0590*/ { "ks" , 214 }, + /*0591*/ { "ku" , 239 }, + /*0592*/ { "kv" , 228 }, + /*0593*/ { "kw" , 90 }, + /*0594*/ { "ky" , 225 }, + /*0595*/ { "la" , 245 }, + /*0596*/ { "lb" , 253 }, + /*0597*/ { "lg" , 256 }, + /*0598*/ { "li" , 248 }, + /*0599*/ { "ln" , 249 }, + /*0600*/ { "lo" , 244 }, + /*0601*/ { "lt" , 250 }, + /*0602*/ { "lu" , 255 }, + /*0603*/ { "lv" , 246 }, + /*0604*/ { "mg" , 282 }, + /*0605*/ { "mh" , 264 }, + /*0606*/ { "mi" , 269 }, + /*0607*/ { "mk" , 261 }, + /*0608*/ { "ml" , 267 }, + /*0609*/ { "mn" , 288 }, + /*0610*/ { "mr" , 271 }, + /*0611*/ { "ms" , 273 }, + /*0612*/ { "mt" , 283 }, + /*0613*/ { "my" , 65 }, + /*0614*/ { "na" , 300 }, + /*0615*/ { "nb" , 312 }, + /*0616*/ { "nd" , 303 }, + /*0617*/ { "ne" , 306 }, + /*0618*/ { "ng" , 304 }, + /*0619*/ { "nl" , 115 }, + /*0620*/ { "nn" , 311 }, + /*0621*/ { "no" , 315 }, + /*0622*/ { "nr" , 302 }, + /*0623*/ { "nv" , 301 }, + /*0624*/ { "ny" , 320 }, + /*0625*/ { "oc" , 325 }, + /*0626*/ { "oj" , 326 }, + /*0627*/ { "om" , 328 }, + /*0628*/ { "or" , 327 }, + /*0629*/ { "os" , 330 }, + /*0630*/ { "pa" , 337 }, + /*0631*/ { "pi" , 344 }, + /*0632*/ { "pl" , 345 }, + /*0633*/ { "ps" , 350 }, + /*0634*/ { "pt" , 347 }, + /*0635*/ { "qu" , 351 }, + /*0636*/ { "rm" , 356 }, + /*0637*/ { "rn" , 359 }, + /*0638*/ { "ro" , 358 }, + /*0639*/ { "ru" , 361 }, + /*0640*/ { "rw" , 224 }, + /*0641*/ { "sa" , 368 }, + /*0642*/ { "sc" , 400 }, + /*0643*/ { "sd" , 393 }, + /*0644*/ { "se" , 386 }, + /*0645*/ { "sg" , 363 }, + /*0646*/ { "si" , 379 }, + /*0647*/ { "sk" , 383 }, + /*0648*/ { "sl" , 384 }, + /*0649*/ { "sm" , 390 }, + /*0650*/ { "sn" , 392 }, + /*0651*/ { "so" , 396 }, + /*0652*/ { "sq" , 12 }, + /*0653*/ { "sr" , 402 }, + /*0654*/ { "ss" , 405 }, + /*0655*/ { "st" , 398 }, + /*0656*/ { "su" , 407 }, + /*0657*/ { "sv" , 411 }, + /*0658*/ { "sw" , 410 }, + /*0659*/ { "ta" , 416 }, + /*0660*/ { "te" , 418 }, + /*0661*/ { "tg" , 422 }, + /*0662*/ { "th" , 424 }, + /*0663*/ { "ti" , 427 }, + /*0664*/ { "tk" , 439 }, + /*0665*/ { "tl" , 423 }, + /*0666*/ { "tn" , 437 }, + /*0667*/ { "to" , 434 }, + /*0668*/ { "tr" , 442 }, + /*0669*/ { "ts" , 438 }, + /*0670*/ { "tt" , 417 }, + /*0671*/ { "tw" , 445 }, + /*0672*/ { "ty" , 414 }, + /*0673*/ { "ug" , 449 }, + /*0674*/ { "uk" , 450 }, + /*0675*/ { "ur" , 453 }, + /*0676*/ { "uz" , 454 }, + /*0677*/ { "ve" , 456 }, + /*0678*/ { "vi" , 457 }, + /*0679*/ { "vo" , 458 }, + /*0680*/ { "wa" , 466 }, + /*0681*/ { "wo" , 467 }, + /*0682*/ { "xh" , 469 }, + /*0683*/ { "yi" , 472 }, + /*0684*/ { "yo" , 473 }, + /*0685*/ { "za" , 478 }, + /*0686*/ { "zh" , 78 }, + /*0687*/ { "zu" , 480 }, + { "", 0 } +}; + +static int lang_table_compare(const void *lhs, const void *rhs) +{ + return strcmp(lhs, ((const LangEntry *)rhs)->str); +} + +const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace) +{ + int i; + const LangEntry *entry = NULL; + const int NB_CODESPACES = FF_ARRAY_ELEMS(lang_table_counts); + + if (target_codespace >= NB_CODESPACES) + return NULL; + + for (i=0; !entry && i= lang_table + lang_table_offsets[target_codespace] && + entry < lang_table + lang_table_offsets[target_codespace] + lang_table_counts[target_codespace]) + return entry->str; + else + entry = lang_table + entry->next_equivalent; + + if (target_codespace == AV_LANG_ISO639_2_TERM) + return av_convert_lang_to(lang, AV_LANG_ISO639_2_BIBL); + + return NULL; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avlanguage.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avlanguage.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,11 @@ +libavformat/avlanguage.o libavformat/avlanguage.o: \ + libavformat/avlanguage.c libavformat/avlanguage.h libavutil/avstring.h \ + libavutil/attributes.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/common.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avlanguage.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avlanguage.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,39 @@ +/* + * Cyril Comparon, Larbi Joubala, Resonate-MP4 2009 + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVLANGUAGE_H +#define AVFORMAT_AVLANGUAGE_H + +/** + * Known language codespaces + */ +enum AVLangCodespace { + AV_LANG_ISO639_2_BIBL, /** 3-char bibliographic language codes as per ISO-IEC 639-2 */ + AV_LANG_ISO639_2_TERM, /** 3-char terminologic language codes as per ISO-IEC 639-2 */ + AV_LANG_ISO639_1 /** 2-char code of language as per ISO/IEC 639-1 */ +}; + +/** + * Convert a language code to a target codespace. The source codespace is guessed. + * @return NULL if the provided lang is null or invalid. + */ +const char *av_convert_lang_to(const char *lang, enum AVLangCodespace target_codespace); + +#endif /* AVFORMAT_AVLANGUAGE_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avlanguage.o Binary file ffmpeg/libavformat/avlanguage.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avr.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,100 @@ +/* + * AVR demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int avr_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('2', 'B', 'I', 'T')) + return AVPROBE_SCORE_MAX / 2; + return 0; +} + +static int avr_read_header(AVFormatContext *s) +{ + uint16_t chan, sign, bps; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + avio_skip(s->pb, 4); // magic + avio_skip(s->pb, 8); // sample_name + + chan = avio_rb16(s->pb); + if (!chan) { + st->codec->channels = 1; + } else if (chan == 0xFFFFu) { + st->codec->channels = 2; + } else { + avpriv_request_sample(s, "chan %d", chan); + return AVERROR_PATCHWELCOME; + } + + st->codec->bits_per_coded_sample = bps = avio_rb16(s->pb); + + sign = avio_rb16(s->pb); + + avio_skip(s->pb, 2); // loop + avio_skip(s->pb, 2); // midi + avio_skip(s->pb, 1); // replay speed + + st->codec->sample_rate = avio_rb24(s->pb); + avio_skip(s->pb, 4 * 3); + avio_skip(s->pb, 2 * 3); + avio_skip(s->pb, 20); + avio_skip(s->pb, 64); + + if (!sign && bps == 8) { + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + } else if (!sign && bps == 16) { + st->codec->codec_id = AV_CODEC_ID_PCM_U16BE; + } else if (sign == 0xFFFFu && bps == 8) { + st->codec->codec_id = AV_CODEC_ID_PCM_S8; + } else if (sign == 0xFFFFu && bps == 16) { + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else { + avpriv_request_sample(s, "bits per sample %d", bps); + return AVERROR_PATCHWELCOME; + } + + st->codec->block_align = bps * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + return 0; +} + +AVInputFormat ff_avr_demuxer = { + .name = "avr", + .long_name = NULL_IF_CONFIG_SMALL("AVR (Audio Visual Research)"), + .read_probe = avr_probe, + .read_header = avr_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "avr", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avr.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avr.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/avr.o libavformat/avr.o: libavformat/avr.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avr.o Binary file ffmpeg/libavformat/avr.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avs.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,232 @@ +/* + * AVS demuxer. + * Copyright (c) 2006 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "voc.h" + + +typedef struct avs_format { + VocDecContext voc; + AVStream *st_video; + AVStream *st_audio; + int width; + int height; + int bits_per_sample; + int fps; + int nb_frames; + int remaining_frame_size; + int remaining_audio_size; +} AvsFormat; + +typedef enum avs_block_type { + AVS_NONE = 0x00, + AVS_VIDEO = 0x01, + AVS_AUDIO = 0x02, + AVS_PALETTE = 0x03, + AVS_GAME_DATA = 0x04, +} AvsBlockType; + +static int avs_probe(AVProbeData * p) +{ + const uint8_t *d; + + d = p->buf; + if (d[0] == 'w' && d[1] == 'W' && d[2] == 0x10 && d[3] == 0) + return 55; + + return 0; +} + +static int avs_read_header(AVFormatContext * s) +{ + AvsFormat *avs = s->priv_data; + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + avio_skip(s->pb, 4); + avs->width = avio_rl16(s->pb); + avs->height = avio_rl16(s->pb); + avs->bits_per_sample = avio_rl16(s->pb); + avs->fps = avio_rl16(s->pb); + avs->nb_frames = avio_rl32(s->pb); + avs->remaining_frame_size = 0; + avs->remaining_audio_size = 0; + + avs->st_video = avs->st_audio = NULL; + + if (avs->width != 318 || avs->height != 198) + av_log(s, AV_LOG_ERROR, "This avs pretend to be %dx%d " + "when the avs format is supposed to be 318x198 only.\n", + avs->width, avs->height); + + return 0; +} + +static int +avs_read_video_packet(AVFormatContext * s, AVPacket * pkt, + AvsBlockType type, int sub_type, int size, + uint8_t * palette, int palette_size) +{ + AvsFormat *avs = s->priv_data; + int ret; + + ret = av_new_packet(pkt, size + palette_size); + if (ret < 0) + return ret; + + if (palette_size) { + pkt->data[0] = 0x00; + pkt->data[1] = 0x03; + pkt->data[2] = palette_size & 0xFF; + pkt->data[3] = (palette_size >> 8) & 0xFF; + memcpy(pkt->data + 4, palette, palette_size - 4); + } + + pkt->data[palette_size + 0] = sub_type; + pkt->data[palette_size + 1] = type; + pkt->data[palette_size + 2] = size & 0xFF; + pkt->data[palette_size + 3] = (size >> 8) & 0xFF; + ret = avio_read(s->pb, pkt->data + palette_size + 4, size - 4) + 4; + if (ret < size) { + av_free_packet(pkt); + return AVERROR(EIO); + } + + pkt->size = ret + palette_size; + pkt->stream_index = avs->st_video->index; + if (sub_type == 0) + pkt->flags |= AV_PKT_FLAG_KEY; + + return 0; +} + +static int avs_read_audio_packet(AVFormatContext * s, AVPacket * pkt) +{ + AvsFormat *avs = s->priv_data; + int ret, size; + + size = avio_tell(s->pb); + ret = ff_voc_get_packet(s, pkt, avs->st_audio, avs->remaining_audio_size); + size = avio_tell(s->pb) - size; + avs->remaining_audio_size -= size; + + if (ret == AVERROR(EIO)) + return 0; /* this indicate EOS */ + if (ret < 0) + return ret; + + pkt->stream_index = avs->st_audio->index; + pkt->flags |= AV_PKT_FLAG_KEY; + + return size; +} + +static int avs_read_packet(AVFormatContext * s, AVPacket * pkt) +{ + AvsFormat *avs = s->priv_data; + int sub_type = 0, size = 0; + AvsBlockType type = AVS_NONE; + int palette_size = 0; + uint8_t palette[4 + 3 * 256]; + int ret; + + if (avs->remaining_audio_size > 0) + if (avs_read_audio_packet(s, pkt) > 0) + return 0; + + while (1) { + if (avs->remaining_frame_size <= 0) { + if (!avio_rl16(s->pb)) /* found EOF */ + return AVERROR(EIO); + avs->remaining_frame_size = avio_rl16(s->pb) - 4; + } + + while (avs->remaining_frame_size > 0) { + sub_type = avio_r8(s->pb); + type = avio_r8(s->pb); + size = avio_rl16(s->pb); + if (size < 4) + return AVERROR_INVALIDDATA; + avs->remaining_frame_size -= size; + + switch (type) { + case AVS_PALETTE: + if (size - 4 > sizeof(palette)) + return AVERROR_INVALIDDATA; + ret = avio_read(s->pb, palette, size - 4); + if (ret < size - 4) + return AVERROR(EIO); + palette_size = size; + break; + + case AVS_VIDEO: + if (!avs->st_video) { + avs->st_video = avformat_new_stream(s, NULL); + if (avs->st_video == NULL) + return AVERROR(ENOMEM); + avs->st_video->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avs->st_video->codec->codec_id = AV_CODEC_ID_AVS; + avs->st_video->codec->width = avs->width; + avs->st_video->codec->height = avs->height; + avs->st_video->codec->bits_per_coded_sample=avs->bits_per_sample; + avs->st_video->nb_frames = avs->nb_frames; +#if FF_API_R_FRAME_RATE + avs->st_video->r_frame_rate = +#endif + avs->st_video->avg_frame_rate = (AVRational){avs->fps, 1}; + } + return avs_read_video_packet(s, pkt, type, sub_type, size, + palette, palette_size); + + case AVS_AUDIO: + if (!avs->st_audio) { + avs->st_audio = avformat_new_stream(s, NULL); + if (avs->st_audio == NULL) + return AVERROR(ENOMEM); + avs->st_audio->codec->codec_type = AVMEDIA_TYPE_AUDIO; + } + avs->remaining_audio_size = size - 4; + size = avs_read_audio_packet(s, pkt); + if (size != 0) + return size; + break; + + default: + avio_skip(s->pb, size - 4); + } + } + } +} + +static int avs_read_close(AVFormatContext * s) +{ + return 0; +} + +AVInputFormat ff_avs_demuxer = { + .name = "avs", + .long_name = NULL_IF_CONFIG_SMALL("AVS"), + .priv_data_size = sizeof(AvsFormat), + .read_probe = avs_probe, + .read_header = avs_read_header, + .read_packet = avs_read_packet, + .read_close = avs_read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avs.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/avs.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/avs.o libavformat/avs.o: libavformat/avs.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/voc.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/avs.o Binary file ffmpeg/libavformat/avs.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bethsoftvid.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bethsoftvid.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,296 @@ +/* + * Bethsoft VID format Demuxer + * Copyright (c) 2007 Nicholas Tung + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @brief Bethesda Softworks VID (.vid) file demuxer + * @author Nicholas Tung [ntung (at. ntung com] (2007-03) + * @see http://wiki.multimedia.cx/index.php?title=Bethsoft_VID + * @see http://www.svatopluk.com/andux/docs/dfvid.html + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "libavcodec/bethsoftvideo.h" + +#define BVID_PALETTE_SIZE 3 * 256 + +#define DEFAULT_SAMPLE_RATE 11111 + +typedef struct BVID_DemuxContext +{ + int nframes; + int sample_rate; /**< audio sample rate */ + int width; /**< video width */ + int height; /**< video height */ + /** delay value between frames, added to individual frame delay. + * custom units, which will be added to other custom units (~=16ms according + * to free, unofficial documentation) */ + int bethsoft_global_delay; + int video_index; /**< video stream index */ + int audio_index; /**< audio stream index */ + uint8_t *palette; + + int is_finished; + +} BVID_DemuxContext; + +static int vid_probe(AVProbeData *p) +{ + // little-endian VID tag, file starts with "VID\0" + if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int vid_read_header(AVFormatContext *s) +{ + BVID_DemuxContext *vid = s->priv_data; + AVIOContext *pb = s->pb; + + /* load main header. Contents: + * bytes: 'V' 'I' 'D' + * int16s: always_512, nframes, width, height, delay, always_14 + */ + avio_skip(pb, 5); + vid->nframes = avio_rl16(pb); + vid->width = avio_rl16(pb); + vid->height = avio_rl16(pb); + vid->bethsoft_global_delay = avio_rl16(pb); + avio_rl16(pb); + + // wait until the first packet to create each stream + vid->video_index = -1; + vid->audio_index = -1; + vid->sample_rate = DEFAULT_SAMPLE_RATE; + s->ctx_flags |= AVFMTCTX_NOHEADER; + + return 0; +} + +#define BUFFER_PADDING_SIZE 1000 +static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, + uint8_t block_type, AVFormatContext *s) +{ + uint8_t * vidbuf_start = NULL; + int vidbuf_nbytes = 0; + int code; + int bytes_copied = 0; + int position, duration, npixels; + unsigned int vidbuf_capacity; + int ret = 0; + AVStream *st; + + if (vid->video_index < 0) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + vid->video_index = st->index; + if (vid->audio_index < 0) { + avpriv_request_sample(s, "Using default video time base since " + "having no audio packet before the first " + "video packet"); + } + avpriv_set_pts_info(st, 64, 185, vid->sample_rate); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_BETHSOFTVID; + st->codec->width = vid->width; + st->codec->height = vid->height; + } + st = s->streams[vid->video_index]; + npixels = st->codec->width * st->codec->height; + + vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE); + if(!vidbuf_start) + return AVERROR(ENOMEM); + + // save the file position for the packet, include block type + position = avio_tell(pb) - 1; + + vidbuf_start[vidbuf_nbytes++] = block_type; + + // get the current packet duration + duration = vid->bethsoft_global_delay + avio_rl16(pb); + + // set the y offset if it exists (decoder header data should be in data section) + if(block_type == VIDEO_YOFF_P_FRAME){ + if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], 2) != 2) { + ret = AVERROR(EIO); + goto fail; + } + vidbuf_nbytes += 2; + } + + do{ + vidbuf_start = av_fast_realloc(vidbuf_start, &vidbuf_capacity, vidbuf_nbytes + BUFFER_PADDING_SIZE); + if(!vidbuf_start) + return AVERROR(ENOMEM); + + code = avio_r8(pb); + vidbuf_start[vidbuf_nbytes++] = code; + + if(code >= 0x80){ // rle sequence + if(block_type == VIDEO_I_FRAME) + vidbuf_start[vidbuf_nbytes++] = avio_r8(pb); + } else if(code){ // plain sequence + if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], code) != code) { + ret = AVERROR(EIO); + goto fail; + } + vidbuf_nbytes += code; + } + bytes_copied += code & 0x7F; + if(bytes_copied == npixels){ // sometimes no stop character is given, need to keep track of bytes copied + // may contain a 0 byte even if read all pixels + if(avio_r8(pb)) + avio_seek(pb, -1, SEEK_CUR); + break; + } + if (bytes_copied > npixels) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + } while(code); + + // copy data into packet + if ((ret = av_new_packet(pkt, vidbuf_nbytes)) < 0) + goto fail; + memcpy(pkt->data, vidbuf_start, vidbuf_nbytes); + av_free(vidbuf_start); + + pkt->pos = position; + pkt->stream_index = vid->video_index; + pkt->duration = duration; + if (block_type == VIDEO_I_FRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + + /* if there is a new palette available, add it to packet side data */ + if (vid->palette) { + uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, + BVID_PALETTE_SIZE); + if (pdata) + memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); + av_freep(&vid->palette); + } + + vid->nframes--; // used to check if all the frames were read + return 0; +fail: + av_free(vidbuf_start); + return ret; +} + +static int vid_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + BVID_DemuxContext *vid = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char block_type; + int audio_length; + int ret_value; + + if(vid->is_finished || url_feof(pb)) + return AVERROR_EOF; + + block_type = avio_r8(pb); + switch(block_type){ + case PALETTE_BLOCK: + if (vid->palette) { + av_log(s, AV_LOG_WARNING, "discarding unused palette\n"); + av_freep(&vid->palette); + } + vid->palette = av_malloc(BVID_PALETTE_SIZE); + if (!vid->palette) + return AVERROR(ENOMEM); + if (avio_read(pb, vid->palette, BVID_PALETTE_SIZE) != BVID_PALETTE_SIZE) { + av_freep(&vid->palette); + return AVERROR(EIO); + } + return vid_read_packet(s, pkt); + + case FIRST_AUDIO_BLOCK: + avio_rl16(pb); + // soundblaster DAC used for sample rate, as on specification page (link above) + vid->sample_rate = 1000000 / (256 - avio_r8(pb)); + case AUDIO_BLOCK: + if (vid->audio_index < 0) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + vid->audio_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->bits_per_coded_sample = 8; + st->codec->sample_rate = vid->sample_rate; + st->codec->bit_rate = 8 * st->codec->sample_rate; + st->start_time = 0; + avpriv_set_pts_info(st, 64, 1, vid->sample_rate); + } + audio_length = avio_rl16(pb); + if ((ret_value = av_get_packet(pb, pkt, audio_length)) != audio_length) { + if (ret_value < 0) + return ret_value; + av_log(s, AV_LOG_ERROR, "incomplete audio block\n"); + return AVERROR(EIO); + } + pkt->stream_index = vid->audio_index; + pkt->duration = audio_length; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; + + case VIDEO_P_FRAME: + case VIDEO_YOFF_P_FRAME: + case VIDEO_I_FRAME: + return read_frame(vid, pb, pkt, block_type, s); + + case EOF_BLOCK: + if(vid->nframes != 0) + av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n"); + vid->is_finished = 1; + return AVERROR(EIO); + default: + av_log(s, AV_LOG_ERROR, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n", + block_type, block_type, block_type); + return AVERROR_INVALIDDATA; + } +} + +static int vid_read_close(AVFormatContext *s) +{ + BVID_DemuxContext *vid = s->priv_data; + av_freep(&vid->palette); + return 0; +} + +AVInputFormat ff_bethsoftvid_demuxer = { + .name = "bethsoftvid", + .long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), + .priv_data_size = sizeof(BVID_DemuxContext), + .read_probe = vid_probe, + .read_header = vid_read_header, + .read_packet = vid_read_packet, + .read_close = vid_read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bethsoftvid.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bethsoftvid.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/bethsoftvid.o libavformat/bethsoftvid.o: \ + libavformat/bethsoftvid.c libavutil/channel_layout.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavcodec/bethsoftvideo.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bethsoftvid.o Binary file ffmpeg/libavformat/bethsoftvid.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bfi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bfi.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,173 @@ +/* + * Brute Force & Ignorance (BFI) demuxer + * Copyright (c) 2008 Sisir Koppaka + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @brief Brute Force & Ignorance (.bfi) file demuxer + * @author Sisir Koppaka ( sisir.koppaka at gmail dot com ) + * @see http://wiki.multimedia.cx/index.php?title=BFI + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +typedef struct BFIContext { + int nframes; + int audio_frame; + int video_frame; + int video_size; + int avflag; +} BFIContext; + +static int bfi_probe(AVProbeData * p) +{ + /* Check file header */ + if (AV_RL32(p->buf) == MKTAG('B', 'F', '&', 'I')) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +static int bfi_read_header(AVFormatContext * s) +{ + BFIContext *bfi = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *vstream; + AVStream *astream; + int fps, chunk_header; + + /* Initialize the video codec... */ + vstream = avformat_new_stream(s, NULL); + if (!vstream) + return AVERROR(ENOMEM); + + /* Initialize the audio codec... */ + astream = avformat_new_stream(s, NULL); + if (!astream) + return AVERROR(ENOMEM); + + /* Set the total number of frames. */ + avio_skip(pb, 8); + chunk_header = avio_rl32(pb); + bfi->nframes = avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + avio_rl32(pb); + fps = avio_rl32(pb); + avio_skip(pb, 12); + vstream->codec->width = avio_rl32(pb); + vstream->codec->height = avio_rl32(pb); + + /*Load the palette to extradata */ + avio_skip(pb, 8); + vstream->codec->extradata = av_malloc(768); + vstream->codec->extradata_size = 768; + avio_read(pb, vstream->codec->extradata, + vstream->codec->extradata_size); + + astream->codec->sample_rate = avio_rl32(pb); + + /* Set up the video codec... */ + avpriv_set_pts_info(vstream, 32, 1, fps); + vstream->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vstream->codec->codec_id = AV_CODEC_ID_BFI; + vstream->codec->pix_fmt = AV_PIX_FMT_PAL8; + vstream->nb_frames = + vstream->duration = bfi->nframes; + + /* Set up the audio codec now... */ + astream->codec->codec_type = AVMEDIA_TYPE_AUDIO; + astream->codec->codec_id = AV_CODEC_ID_PCM_U8; + astream->codec->channels = 1; + astream->codec->channel_layout = AV_CH_LAYOUT_MONO; + astream->codec->bits_per_coded_sample = 8; + astream->codec->bit_rate = + astream->codec->sample_rate * astream->codec->bits_per_coded_sample; + avio_seek(pb, chunk_header - 3, SEEK_SET); + avpriv_set_pts_info(astream, 64, 1, astream->codec->sample_rate); + return 0; +} + + +static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt) +{ + BFIContext *bfi = s->priv_data; + AVIOContext *pb = s->pb; + int ret, audio_offset, video_offset, chunk_size, audio_size = 0; + if (bfi->nframes == 0 || url_feof(pb)) { + return AVERROR_EOF; + } + + /* If all previous chunks were completely read, then find a new one... */ + if (!bfi->avflag) { + uint32_t state = 0; + while(state != MKTAG('S','A','V','I')){ + if (url_feof(pb)) + return AVERROR(EIO); + state = 256*state + avio_r8(pb); + } + /* Now that the chunk's location is confirmed, we proceed... */ + chunk_size = avio_rl32(pb); + avio_rl32(pb); + audio_offset = avio_rl32(pb); + avio_rl32(pb); + video_offset = avio_rl32(pb); + audio_size = video_offset - audio_offset; + bfi->video_size = chunk_size - video_offset; + + //Tossing an audio packet at the audio decoder. + ret = av_get_packet(pb, pkt, audio_size); + if (ret < 0) + return ret; + + pkt->pts = bfi->audio_frame; + bfi->audio_frame += ret; + } + + else { + + //Tossing a video packet at the video decoder. + ret = av_get_packet(pb, pkt, bfi->video_size); + if (ret < 0) + return ret; + + pkt->pts = bfi->video_frame; + bfi->video_frame += bfi->video_size ? ret / bfi->video_size : 1; + + /* One less frame to read. A cursory decrement. */ + bfi->nframes--; + } + + bfi->avflag = !bfi->avflag; + pkt->stream_index = bfi->avflag; + return ret; +} + +AVInputFormat ff_bfi_demuxer = { + .name = "bfi", + .long_name = NULL_IF_CONFIG_SMALL("Brute Force & Ignorance"), + .priv_data_size = sizeof(BFIContext), + .read_probe = bfi_probe, + .read_header = bfi_read_header, + .read_packet = bfi_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bfi.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bfi.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/bfi.o libavformat/bfi.o: libavformat/bfi.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bfi.o Binary file ffmpeg/libavformat/bfi.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bink.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bink.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,287 @@ +/* + * Bink demuxer + * Copyright (c) 2008-2010 Peter Ross (pross@xvid.org) + * Copyright (c) 2009 Daniel Verkamp (daniel@drv.nu) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Bink demuxer + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=Bink_Container + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +enum BinkAudFlags { + BINK_AUD_16BITS = 0x4000, ///< prefer 16-bit output + BINK_AUD_STEREO = 0x2000, + BINK_AUD_USEDCT = 0x1000, +}; + +#define BINK_EXTRADATA_SIZE 1 +#define BINK_MAX_AUDIO_TRACKS 256 +#define BINK_MAX_WIDTH 7680 +#define BINK_MAX_HEIGHT 4800 + +typedef struct { + uint32_t file_size; + + uint32_t num_audio_tracks; + int current_track; ///< audio track to return in next packet + int64_t video_pts; + int64_t audio_pts[BINK_MAX_AUDIO_TRACKS]; + + uint32_t remain_packet_size; +} BinkDemuxContext; + +static int probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if ( b[0] == 'B' && b[1] == 'I' && b[2] == 'K' && + (b[3] == 'b' || b[3] == 'f' || b[3] == 'g' || b[3] == 'h' || b[3] == 'i') && + AV_RL32(b+8) > 0 && // num_frames + AV_RL32(b+20) > 0 && AV_RL32(b+20) <= BINK_MAX_WIDTH && + AV_RL32(b+24) > 0 && AV_RL32(b+24) <= BINK_MAX_HEIGHT && + AV_RL32(b+28) > 0 && AV_RL32(b+32) > 0) // fps num,den + return AVPROBE_SCORE_MAX; + return 0; +} + +static int read_header(AVFormatContext *s) +{ + BinkDemuxContext *bink = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t fps_num, fps_den; + AVStream *vst, *ast; + unsigned int i; + uint32_t pos, next_pos; + uint16_t flags; + int keyframe; + + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + + vst->codec->codec_tag = avio_rl32(pb); + + bink->file_size = avio_rl32(pb) + 8; + vst->duration = avio_rl32(pb); + + if (vst->duration > 1000000) { + av_log(s, AV_LOG_ERROR, "invalid header: more than 1000000 frames\n"); + return AVERROR(EIO); + } + + if (avio_rl32(pb) > bink->file_size) { + av_log(s, AV_LOG_ERROR, + "invalid header: largest frame size greater than file size\n"); + return AVERROR(EIO); + } + + avio_skip(pb, 4); + + vst->codec->width = avio_rl32(pb); + vst->codec->height = avio_rl32(pb); + + fps_num = avio_rl32(pb); + fps_den = avio_rl32(pb); + if (fps_num == 0 || fps_den == 0) { + av_log(s, AV_LOG_ERROR, "invalid header: invalid fps (%d/%d)\n", fps_num, fps_den); + return AVERROR(EIO); + } + avpriv_set_pts_info(vst, 64, fps_den, fps_num); + vst->avg_frame_rate = av_inv_q(vst->time_base); + + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_BINKVIDEO; + vst->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + vst->codec->extradata_size = 4; + avio_read(pb, vst->codec->extradata, 4); + + bink->num_audio_tracks = avio_rl32(pb); + + if (bink->num_audio_tracks > BINK_MAX_AUDIO_TRACKS) { + av_log(s, AV_LOG_ERROR, + "invalid header: more than "AV_STRINGIFY(BINK_MAX_AUDIO_TRACKS)" audio tracks (%d)\n", + bink->num_audio_tracks); + return AVERROR(EIO); + } + + if (bink->num_audio_tracks) { + avio_skip(pb, 4 * bink->num_audio_tracks); + + for (i = 0; i < bink->num_audio_tracks; i++) { + ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_tag = 0; + ast->codec->sample_rate = avio_rl16(pb); + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + flags = avio_rl16(pb); + ast->codec->codec_id = flags & BINK_AUD_USEDCT ? + AV_CODEC_ID_BINKAUDIO_DCT : AV_CODEC_ID_BINKAUDIO_RDFT; + if (flags & BINK_AUD_STEREO) { + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + ast->codec->channels = 1; + ast->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + ast->codec->extradata = av_mallocz(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!ast->codec->extradata) + return AVERROR(ENOMEM); + ast->codec->extradata_size = 4; + AV_WL32(ast->codec->extradata, vst->codec->codec_tag); + } + + for (i = 0; i < bink->num_audio_tracks; i++) + s->streams[i + 1]->id = avio_rl32(pb); + } + + /* frame index table */ + next_pos = avio_rl32(pb); + for (i = 0; i < vst->duration; i++) { + pos = next_pos; + if (i == vst->duration - 1) { + next_pos = bink->file_size; + keyframe = 0; + } else { + next_pos = avio_rl32(pb); + keyframe = pos & 1; + } + pos &= ~1; + next_pos &= ~1; + + if (next_pos <= pos) { + av_log(s, AV_LOG_ERROR, "invalid frame index table\n"); + return AVERROR(EIO); + } + av_add_index_entry(vst, pos, i, next_pos - pos, 0, + keyframe ? AVINDEX_KEYFRAME : 0); + } + + avio_skip(pb, 4); + + bink->current_track = -1; + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + BinkDemuxContext *bink = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + + if (bink->current_track < 0) { + int index_entry; + AVStream *st = s->streams[0]; // stream 0 is video stream with index + + if (bink->video_pts >= st->duration) + return AVERROR_EOF; + + index_entry = av_index_search_timestamp(st, bink->video_pts, + AVSEEK_FLAG_ANY); + if (index_entry < 0) { + av_log(s, AV_LOG_ERROR, + "could not find index entry for frame %"PRId64"\n", + bink->video_pts); + return AVERROR(EIO); + } + + bink->remain_packet_size = st->index_entries[index_entry].size; + bink->current_track = 0; + } + + while (bink->current_track < bink->num_audio_tracks) { + uint32_t audio_size = avio_rl32(pb); + if (audio_size > bink->remain_packet_size - 4) { + av_log(s, AV_LOG_ERROR, + "frame %"PRId64": audio size in header (%u) > size of packet left (%u)\n", + bink->video_pts, audio_size, bink->remain_packet_size); + return AVERROR(EIO); + } + bink->remain_packet_size -= 4 + audio_size; + bink->current_track++; + if (audio_size >= 4) { + /* get one audio packet per track */ + if ((ret = av_get_packet(pb, pkt, audio_size)) < 0) + return ret; + pkt->stream_index = bink->current_track; + pkt->pts = bink->audio_pts[bink->current_track - 1]; + + /* Each audio packet reports the number of decompressed samples + (in bytes). We use this value to calcuate the audio PTS */ + if (pkt->size >= 4) + bink->audio_pts[bink->current_track -1] += + AV_RL32(pkt->data) / (2 * s->streams[bink->current_track]->codec->channels); + return 0; + } else { + avio_skip(pb, audio_size); + } + } + + /* get video packet */ + if ((ret = av_get_packet(pb, pkt, bink->remain_packet_size)) < 0) + return ret; + pkt->stream_index = 0; + pkt->pts = bink->video_pts++; + pkt->flags |= AV_PKT_FLAG_KEY; + + /* -1 instructs the next call to read_packet() to read the next frame */ + bink->current_track = -1; + + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + BinkDemuxContext *bink = s->priv_data; + AVStream *vst = s->streams[0]; + + if (!s->pb->seekable) + return -1; + + /* seek to the first frame */ + if (avio_seek(s->pb, vst->index_entries[0].pos, SEEK_SET) < 0) + return -1; + + bink->video_pts = 0; + memset(bink->audio_pts, 0, sizeof(bink->audio_pts)); + bink->current_track = -1; + return 0; +} + +AVInputFormat ff_bink_demuxer = { + .name = "bink", + .long_name = NULL_IF_CONFIG_SMALL("Bink"), + .priv_data_size = sizeof(BinkDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .read_seek = read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bink.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bink.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/bink.o libavformat/bink.o: libavformat/bink.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bink.o Binary file ffmpeg/libavformat/bink.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bintext.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bintext.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,395 @@ +/* + * Binary text demuxer + * eXtended BINary text (XBIN) demuxer + * Artworx Data Format demuxer + * iCEDraw File demuxer + * Copyright (c) 2010 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Binary text demuxer + * eXtended BINary text (XBIN) demuxer + * Artworx Data Format demuxer + * iCEDraw File demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "sauce.h" +#include "libavcodec/bintext.h" + +typedef struct { + const AVClass *class; + int chars_per_frame; /**< characters to send decoder per frame; + set by private options as characters per second, and then + converted to characters per frame at runtime */ + int width, height; /**< video size (WxH pixels) (private option) */ + AVRational framerate; /**< frames per second (private option) */ + uint64_t fsize; /**< file size less metadata buffer */ +} BinDemuxContext; + +static AVStream * init_stream(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return NULL; + st->codec->codec_tag = 0; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + + if (!bin->width) { + st->codec->width = (80<<3); + st->codec->height = (25<<4); + } + + avpriv_set_pts_info(st, 60, bin->framerate.den, bin->framerate.num); + + /* simulate tty display speed */ + bin->chars_per_frame = FFMAX(av_q2d(st->time_base) * bin->chars_per_frame, 1); + + return st; +} + +#if CONFIG_BINTEXT_DEMUXER | CONFIG_ADF_DEMUXER | CONFIG_IDF_DEMUXER +/** + * Given filesize and width, calculate height (assume font_height of 16) + */ +static void calculate_height(AVCodecContext *avctx, uint64_t fsize) +{ + avctx->height = (fsize / ((avctx->width>>3)*2)) << 4; +} +#endif + +#if CONFIG_BINTEXT_DEMUXER +static const uint8_t next_magic[]={ + 0x1A, 0x1B, '[', '0', ';', '3', '0', ';', '4', '0', 'm', 'N', 'E', 'X', 'T', 0x00 +}; + +static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize) +{ + AVIOContext *pb = avctx->pb; + char buf[36]; + int len; + uint64_t start_pos = avio_size(pb) - 256; + + avio_seek(pb, start_pos, SEEK_SET); + if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic)) + return -1; + if (memcmp(buf, next_magic, sizeof(next_magic))) + return -1; + if (avio_r8(pb) != 0x01) + return -1; + + *fsize -= 256; + +#define GET_EFI2_META(name,size) \ + len = avio_r8(pb); \ + if (len < 1 || len > size) \ + return -1; \ + if (avio_read(pb, buf, size) == size && *buf) { \ + buf[len] = 0; \ + av_dict_set(&avctx->metadata, name, buf, 0); \ + } + + GET_EFI2_META("filename", 12) + GET_EFI2_META("author", 20) + GET_EFI2_META("publisher", 20) + GET_EFI2_META("title", 35) + + return 0; +} + +static void predict_width(AVCodecContext *avctx, uint64_t fsize, int got_width) +{ + /** attempt to guess width */ + if (!got_width) + avctx->width = fsize > 4000 ? (160<<3) : (80<<3); +} + +static int bintext_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + + AVStream *st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_BINTEXT; + + st->codec->extradata_size = 2; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = 0; + + if (pb->seekable) { + int got_width = 0; + bin->fsize = avio_size(pb); + if (ff_sauce_read(s, &bin->fsize, &got_width, 0) < 0) + next_tag_read(s, &bin->fsize); + if (!bin->width) { + predict_width(st->codec, bin->fsize, got_width); + calculate_height(st->codec, bin->fsize); + } + avio_seek(pb, 0, SEEK_SET); + } + return 0; +} +#endif /* CONFIG_BINTEXT_DEMUXER */ + +#if CONFIG_XBIN_DEMUXER +static int xbin_probe(AVProbeData *p) +{ + const uint8_t *d = p->buf; + + if (AV_RL32(d) == MKTAG('X','B','I','N') && d[4] == 0x1A && + AV_RL16(d+5) > 0 && AV_RL16(d+5) <= 160 && + d[9] > 0 && d[9] <= 32) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int xbin_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + char fontheight, flags; + + AVStream *st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 5); + st->codec->width = avio_rl16(pb)<<3; + st->codec->height = avio_rl16(pb); + fontheight = avio_r8(pb); + st->codec->height *= fontheight; + flags = avio_r8(pb); + + st->codec->extradata_size = 2; + if ((flags & BINTEXT_PALETTE)) + st->codec->extradata_size += 48; + if ((flags & BINTEXT_FONT)) + st->codec->extradata_size += fontheight * (flags & 0x10 ? 512 : 256); + st->codec->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT; + + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata[0] = fontheight; + st->codec->extradata[1] = flags; + if (avio_read(pb, st->codec->extradata + 2, st->codec->extradata_size - 2) < 0) + return AVERROR(EIO); + + if (pb->seekable) { + bin->fsize = avio_size(pb) - 9 - st->codec->extradata_size; + ff_sauce_read(s, &bin->fsize, NULL, 0); + avio_seek(pb, 9 + st->codec->extradata_size, SEEK_SET); + } + + return 0; +} +#endif /* CONFIG_XBIN_DEMUXER */ + +#if CONFIG_ADF_DEMUXER +static int adf_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + + if (avio_r8(pb) != 1) + return AVERROR_INVALIDDATA; + + st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_BINTEXT; + + st->codec->extradata_size = 2 + 48 + 4096; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; + + if (avio_read(pb, st->codec->extradata + 2, 24) < 0) + return AVERROR(EIO); + avio_skip(pb, 144); + if (avio_read(pb, st->codec->extradata + 2 + 24, 24) < 0) + return AVERROR(EIO); + if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) + return AVERROR(EIO); + + if (pb->seekable) { + int got_width = 0; + bin->fsize = avio_size(pb) - 1 - 192 - 4096; + st->codec->width = 80<<3; + ff_sauce_read(s, &bin->fsize, &got_width, 0); + if (!bin->width) + calculate_height(st->codec, bin->fsize); + avio_seek(pb, 1 + 192 + 4096, SEEK_SET); + } + return 0; +} +#endif /* CONFIG_ADF_DEMUXER */ + +#if CONFIG_IDF_DEMUXER +static const uint8_t idf_magic[] = { + 0x04, 0x31, 0x2e, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x15, 0x00 +}; + +static int idf_probe(AVProbeData *p) +{ + if (p->buf_size < sizeof(idf_magic)) + return 0; + if (!memcmp(p->buf, idf_magic, sizeof(idf_magic))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int idf_read_header(AVFormatContext *s) +{ + BinDemuxContext *bin = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int got_width = 0; + + if (!pb->seekable) + return AVERROR(EIO); + + st = init_stream(s); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_IDF; + + st->codec->extradata_size = 2 + 48 + 4096; + st->codec->extradata = av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata[0] = 16; + st->codec->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; + + avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET); + + if (avio_read(pb, st->codec->extradata + 2 + 48, 4096) < 0) + return AVERROR(EIO); + if (avio_read(pb, st->codec->extradata + 2, 48) < 0) + return AVERROR(EIO); + + bin->fsize = avio_size(pb) - 12 - 4096 - 48; + ff_sauce_read(s, &bin->fsize, &got_width, 0); + if (!bin->width) + calculate_height(st->codec, bin->fsize); + avio_seek(pb, 12, SEEK_SET); + return 0; +} +#endif /* CONFIG_IDF_DEMUXER */ + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + BinDemuxContext *bin = s->priv_data; + + if (bin->fsize > 0) { + if (av_get_packet(s->pb, pkt, bin->fsize) < 0) + return AVERROR(EIO); + bin->fsize = -1; /* done */ + } else if (!bin->fsize) { + if (url_feof(s->pb)) + return AVERROR(EIO); + if (av_get_packet(s->pb, pkt, bin->chars_per_frame) < 0) + return AVERROR(EIO); + } else { + return AVERROR(EIO); + } + + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +#define OFFSET(x) offsetof(BinDemuxContext, x) +static const AVOption options[] = { + { "linespeed", "set simulated line speed (bytes per second)", OFFSET(chars_per_frame), AV_OPT_TYPE_INT, {.i64 = 6000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}, + { "video_size", "set video size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { "framerate", "set framerate (frames per second)", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +#define CLASS(name) \ +(const AVClass[1]){{ \ + .class_name = name, \ + .item_name = av_default_item_name, \ + .option = options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}} + +#if CONFIG_BINTEXT_DEMUXER +AVInputFormat ff_bintext_demuxer = { + .name = "bin", + .long_name = NULL_IF_CONFIG_SMALL("Binary text"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = bintext_read_header, + .read_packet = read_packet, + .extensions = "bin", + .priv_class = CLASS("Binary text demuxer"), +}; +#endif + +#if CONFIG_XBIN_DEMUXER +AVInputFormat ff_xbin_demuxer = { + .name = "xbin", + .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = xbin_probe, + .read_header = xbin_read_header, + .read_packet = read_packet, + .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), +}; +#endif + +#if CONFIG_ADF_DEMUXER +AVInputFormat ff_adf_demuxer = { + .name = "adf", + .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), + .priv_data_size = sizeof(BinDemuxContext), + .read_header = adf_read_header, + .read_packet = read_packet, + .extensions = "adf", + .priv_class = CLASS("Artworx Data Format demuxer"), +}; +#endif + +#if CONFIG_IDF_DEMUXER +AVInputFormat ff_idf_demuxer = { + .name = "idf", + .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), + .priv_data_size = sizeof(BinDemuxContext), + .read_probe = idf_probe, + .read_header = idf_read_header, + .read_packet = read_packet, + .extensions = "idf", + .priv_class = CLASS("iCE Draw File demuxer"), +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bintext.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bintext.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/bintext.o libavformat/bintext.o: libavformat/bintext.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavutil/opt.h \ + libavutil/rational.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/parseutils.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/sauce.h libavcodec/bintext.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bintext.o Binary file ffmpeg/libavformat/bintext.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bit.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,156 @@ +/* + * G.729 bit format muxer and demuxer + * Copyright (c) 2007-2008 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "internal.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" + +#define MAX_FRAME_SIZE 10 + +#define SYNC_WORD 0x6b21 +#define BIT_0 0x7f +#define BIT_1 0x81 + +static int probe(AVProbeData *p) +{ + int i, j; + + if(p->buf_size < 0x40) + return 0; + + for(i=0; i+3buf_size && i< 10*0x50; ){ + if(AV_RL16(&p->buf[0]) != SYNC_WORD) + return 0; + j=AV_RL16(&p->buf[2]); + if(j!=0x40 && j!=0x50) + return 0; + i+=j; + } + return AVPROBE_SCORE_MAX/2; +} + +static int read_header(AVFormatContext *s) +{ + AVStream* st; + + st=avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id=AV_CODEC_ID_G729; + st->codec->sample_rate=8000; + st->codec->block_align = 16; + st->codec->channels=1; + + avpriv_set_pts_info(st, 64, 1, 100); + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + PutBitContext pbo; + uint16_t buf[8 * MAX_FRAME_SIZE + 2]; + int packet_size; + uint16_t* src=buf; + int i, j, ret; + int64_t pos= avio_tell(pb); + + if(url_feof(pb)) + return AVERROR_EOF; + + avio_rl16(pb); // sync word + packet_size = avio_rl16(pb) / 8; + if(packet_size > MAX_FRAME_SIZE) + return AVERROR_INVALIDDATA; + + ret = avio_read(pb, (uint8_t*)buf, (8 * packet_size) * sizeof(uint16_t)); + if(ret<0) + return ret; + if(ret != 8 * packet_size * sizeof(uint16_t)) + return AVERROR(EIO); + + if (av_new_packet(pkt, packet_size) < 0) + return AVERROR(ENOMEM); + + init_put_bits(&pbo, pkt->data, packet_size); + for(j=0; j < packet_size; j++) + for(i=0; i<8;i++) + put_bits(&pbo,1, AV_RL16(src++) == BIT_1 ? 1 : 0); + + flush_put_bits(&pbo); + + pkt->duration=1; + pkt->pos = pos; + return 0; +} + +AVInputFormat ff_bit_demuxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .extensions = "bit", +}; + +#if CONFIG_MUXERS +static int write_header(AVFormatContext *s) +{ + AVCodecContext *enc = s->streams[0]->codec; + + enc->codec_id = AV_CODEC_ID_G729; + enc->channels = 1; + enc->bits_per_coded_sample = 16; + enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3; + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + GetBitContext gb; + int i; + + avio_wl16(pb, SYNC_WORD); + avio_wl16(pb, 8 * 10); + + init_get_bits(&gb, pkt->data, 8*10); + for(i=0; i< 8 * 10; i++) + avio_wl16(pb, get_bits1(&gb) ? BIT_1 : BIT_0); + + return 0; +} + +AVOutputFormat ff_bit_muxer = { + .name = "bit", + .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .mime_type = "audio/bit", + .extensions = "bit", + .audio_codec = AV_CODEC_ID_G729, + .video_codec = AV_CODEC_ID_NONE, + .write_header = write_header, + .write_packet = write_packet, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bit.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bit.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/bit.o libavformat/bit.o: libavformat/bit.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavcodec/get_bits.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavcodec/put_bits.h libavutil/bswap.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bit.o Binary file ffmpeg/libavformat/bit.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bluray.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bluray.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,235 @@ +/* + * BluRay (libbluray) protocol + * + * Copyright (c) 2012 Petri Hintukainen users.sourceforge.net> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/avstring.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" +#include "libavutil/opt.h" + +#define BLURAY_PROTO_PREFIX "bluray:" +#define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + +typedef struct { + const AVClass *class; + + BLURAY *bd; + + int playlist; + int angle; + int chapter; + /*int region;*/ +} BlurayContext; + +#define OFFSET(x) offsetof(BlurayContext, x) +static const AVOption options[] = { +{"playlist", "", OFFSET(playlist), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 99999, AV_OPT_FLAG_DECODING_PARAM }, +{"angle", "", OFFSET(angle), AV_OPT_TYPE_INT, { .i64=0 }, 0, 0xfe, AV_OPT_FLAG_DECODING_PARAM }, +{"chapter", "", OFFSET(chapter), AV_OPT_TYPE_INT, { .i64=1 }, 1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM }, +/*{"region", "bluray player region code (1 = region A, 2 = region B, 4 = region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, AV_OPT_FLAG_DECODING_PARAM },*/ +{NULL} +}; + +static const AVClass bluray_context_class = { + .class_name = "bluray", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +static int check_disc_info(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + const BLURAY_DISC_INFO *disc_info; + + disc_info = bd_get_disc_info(bd->bd); + if (!disc_info) { + av_log(h, AV_LOG_ERROR, "bd_get_disc_info() failed\n"); + return -1; + } + + if (!disc_info->bluray_detected) { + av_log(h, AV_LOG_ERROR, "BluRay disc not detected\n"); + return -1; + } + + /* AACS */ + if (disc_info->aacs_detected && !disc_info->aacs_handled) { + if (!disc_info->libaacs_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with AACS, install and configure libaacs\n"); + } else { + av_log(h, AV_LOG_ERROR, "Your libaacs can't decrypt this media\n"); + } + return -1; + } + + /* BD+ */ + if (disc_info->bdplus_detected && !disc_info->bdplus_handled) { + /* + if (!disc_info->libbdplus_detected) { + av_log(h, AV_LOG_ERROR, + "Media stream encrypted with BD+, install and configure libbdplus"); + } else { + */ + av_log(h, AV_LOG_ERROR, "Unable to decrypt BD+ encrypted media\n"); + /*}*/ + return -1; + } + + return 0; +} + +static int bluray_close(URLContext *h) +{ + BlurayContext *bd = h->priv_data; + if (bd->bd) { + bd_close(bd->bd); + } + + return 0; +} + +static int bluray_open(URLContext *h, const char *path, int flags) +{ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + + bd->bd = bd_open(diskname, NULL); + if (!bd->bd) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); + return AVERROR(EIO); + } + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { + return AVERROR(EIO); + } + + /* setup player registers */ + /* region code has no effect without menus + if (bd->region > 0 && bd->region < 5) { + av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", bd->region, 'A' + (bd->region - 1)); + bd_set_player_setting(bd->bd, BLURAY_PLAYER_SETTING_REGION_CODE, bd->region); + } + */ + + /* load title list */ + num_title_idx = bd_get_titles(bd->bd, TITLES_RELEVANT, MIN_PLAYLIST_LENGTH); + av_log(h, AV_LOG_INFO, "%d usable playlists:\n", num_title_idx); + if (num_title_idx < 1) { + return AVERROR(EIO); + } + + /* if playlist was not given, select longest playlist */ + if (bd->playlist < 0) { + uint64_t duration = 0; + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, + ((int)(info->duration / 90000) / 3600), + ((int)(info->duration / 90000) % 3600) / 60, + ((int)(info->duration / 90000) % 60)); + + if (info->duration > duration) { + bd->playlist = info->playlist; + duration = info->duration; + } + + bd_free_title_info(info); + } + av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); + } + + /* select playlist */ + if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { + av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); + return AVERROR(EIO); + } + + /* select angle */ + if (bd->angle >= 0) { + bd_select_angle(bd->bd, bd->angle); + } + + /* select chapter */ + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } + + return 0; +} + +static int bluray_read(URLContext *h, unsigned char *buf, int size) +{ + BlurayContext *bd = h->priv_data; + int len; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + len = bd_read(bd->bd, buf, size); + + return len; +} + +static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) +{ + BlurayContext *bd = h->priv_data; + + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return bd_seek(bd->bd, pos); + + case AVSEEK_SIZE: + return bd_get_title_size(bd->bd); + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); + return AVERROR(EINVAL); +} + + +URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, + .url_open = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bmv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bmv.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,139 @@ +/* + * Discworld II BMV demuxer + * Copyright (c) 2011 Konstantin Shishkov. + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +enum BMVFlags { + BMV_NOP = 0, + BMV_END, + BMV_DELTA, + BMV_INTRA, + + BMV_AUDIO = 0x20, +}; + +typedef struct BMVContext { + uint8_t *packet; + int size; + int get_next; + int64_t audio_pos; +} BMVContext; + +static int bmv_read_header(AVFormatContext *s) +{ + AVStream *st, *ast; + BMVContext *c = s->priv_data; + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_BMV_VIDEO; + st->codec->width = 640; + st->codec->height = 429; + st->codec->pix_fmt = AV_PIX_FMT_PAL8; + avpriv_set_pts_info(st, 16, 1, 12); + ast = avformat_new_stream(s, 0); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_BMV_AUDIO; + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + ast->codec->sample_rate = 22050; + avpriv_set_pts_info(ast, 16, 1, 22050); + + c->get_next = 1; + c->audio_pos = 0; + return 0; +} + +static int bmv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + BMVContext *c = s->priv_data; + int type; + void *tmp; + + while (c->get_next) { + if (s->pb->eof_reached) + return AVERROR_EOF; + type = avio_r8(s->pb); + if (type == BMV_NOP) + continue; + if (type == BMV_END) + return AVERROR_EOF; + c->size = avio_rl24(s->pb); + if (!c->size) + return AVERROR_INVALIDDATA; + tmp = av_realloc(c->packet, c->size + 1); + if (!tmp) + return AVERROR(ENOMEM); + c->packet = tmp; + c->packet[0] = type; + if (avio_read(s->pb, c->packet + 1, c->size) != c->size) + return AVERROR(EIO); + if (type & BMV_AUDIO) { + int audio_size = c->packet[1] * 65 + 1; + if (audio_size >= c->size) { + av_log(s, AV_LOG_ERROR, "Reported audio size %d is bigger than packet size (%d)\n", + audio_size, c->size); + return AVERROR_INVALIDDATA; + } + if (av_new_packet(pkt, audio_size) < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, c->packet + 1, pkt->size); + pkt->stream_index = 1; + pkt->pts = c->audio_pos; + pkt->duration = c->packet[1] * 32; + c->audio_pos += pkt->duration; + c->get_next = 0; + return pkt->size; + } else + break; + } + if (av_new_packet(pkt, c->size + 1) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 0; + c->get_next = 1; + memcpy(pkt->data, c->packet, pkt->size); + return pkt->size; +} + +static int bmv_read_close(AVFormatContext *s) +{ + BMVContext *c = s->priv_data; + + av_freep(&c->packet); + + return 0; +} + +AVInputFormat ff_bmv_demuxer = { + .name = "bmv", + .long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV"), + .priv_data_size = sizeof(BMVContext), + .read_header = bmv_read_header, + .read_packet = bmv_read_packet, + .read_close = bmv_read_close, + .extensions = "bmv", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bmv.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/bmv.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/bmv.o libavformat/bmv.o: libavformat/bmv.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/bmv.o Binary file ffmpeg/libavformat/bmv.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/brstm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/brstm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,297 @@ +/* + * BRSTM demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" + +typedef struct BRSTMDemuxContext { + uint32_t block_size; + uint32_t block_count; + uint32_t current_block; + uint32_t samples_per_block; + uint32_t last_block_used_bytes; + uint8_t *table; + uint8_t *adpc; +} BRSTMDemuxContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('R','S','T','M') && + (AV_RL16(p->buf + 4) == 0xFFFE || + AV_RL16(p->buf + 4) == 0xFEFF)) + return AVPROBE_SCORE_MAX / 3 * 2; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + BRSTMDemuxContext *b = s->priv_data; + + av_freep(&b->table); + av_freep(&b->adpc); + + return 0; +} + +static int read_header(AVFormatContext *s) +{ + BRSTMDemuxContext *b = s->priv_data; + int bom, major, minor, codec, chunk; + int64_t pos, h1offset, toffset; + uint32_t size, start, asize; + AVStream *st; + int ret = AVERROR_EOF; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + avio_skip(s->pb, 4); + + bom = avio_rb16(s->pb); + if (bom != 0xFEFF && bom != 0xFFFE) { + av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom); + return AVERROR_INVALIDDATA; + } + if (bom == 0xFFFE) { + avpriv_request_sample(s, "little endian byte order"); + return AVERROR_PATCHWELCOME; + } + + major = avio_r8(s->pb); + minor = avio_r8(s->pb); + avio_skip(s->pb, 4); // size of file + size = avio_rb16(s->pb); + if (size < 14) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, size - 14); + pos = avio_tell(s->pb); + if (avio_rl32(s->pb) != MKTAG('H','E','A','D')) + return AVERROR_INVALIDDATA; + size = avio_rb32(s->pb); + if (size < 256) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 4); // unknown + h1offset = avio_rb32(s->pb); + if (h1offset > size) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 12); + toffset = avio_rb32(s->pb) + 16LL; + if (toffset > size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, pos + h1offset + 8 - avio_tell(s->pb)); + codec = avio_r8(s->pb); + + switch (codec) { + case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR; break; + case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break; + case 2: codec = AV_CODEC_ID_ADPCM_THP; break; + default: + avpriv_request_sample(s, "codec %d", codec); + return AVERROR_PATCHWELCOME; + } + + avio_skip(s->pb, 1); // loop flag + st->codec->codec_id = codec; + st->codec->channels = avio_r8(s->pb); + if (!st->codec->channels) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, 1); // padding + st->codec->sample_rate = avio_rb16(s->pb); + if (!st->codec->sample_rate) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, 2); // padding + avio_skip(s->pb, 4); // loop start sample + st->start_time = 0; + st->duration = avio_rb32(s->pb); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + start = avio_rb32(s->pb); + b->current_block = 0; + b->block_count = avio_rb32(s->pb); + if (b->block_count > UINT16_MAX) { + av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count); + return AVERROR_INVALIDDATA; + } + + b->block_size = avio_rb32(s->pb); + if (b->block_size > UINT16_MAX / st->codec->channels) + return AVERROR_INVALIDDATA; + b->block_size *= st->codec->channels; + + b->samples_per_block = avio_rb32(s->pb); + b->last_block_used_bytes = avio_rb32(s->pb); + if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels) + return AVERROR_INVALIDDATA; + b->last_block_used_bytes *= st->codec->channels; + + avio_skip(s->pb, 4); // last block samples + avio_skip(s->pb, 4); // last block size + + if (codec == AV_CODEC_ID_ADPCM_THP) { + int ch; + + avio_skip(s->pb, pos + toffset - avio_tell(s->pb)); + toffset = avio_rb32(s->pb) + 16LL; + if (toffset > size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, pos + toffset - avio_tell(s->pb)); + b->table = av_mallocz(32 * st->codec->channels); + if (!b->table) + return AVERROR(ENOMEM); + + for (ch = 0; ch < st->codec->channels; ch++) { + if (avio_read(s->pb, b->table + ch * 32, 32) != 32) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, 24); + } + } + + if (size < (avio_tell(s->pb) - pos)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, size - (avio_tell(s->pb) - pos)); + + while (!url_feof(s->pb)) { + chunk = avio_rl32(s->pb); + size = avio_rb32(s->pb); + if (size < 8) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + size -= 8; + switch (chunk) { + case MKTAG('A','D','P','C'): + if (codec != AV_CODEC_ID_ADPCM_THP) + goto skip; + + asize = b->block_count * st->codec->channels * 4; + if (size < asize) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (b->adpc) { + av_log(s, AV_LOG_WARNING, "skipping additonal ADPC chunk\n"); + goto skip; + } else { + b->adpc = av_mallocz(asize); + if (!b->adpc) { + ret = AVERROR(ENOMEM); + goto fail; + } + avio_read(s->pb, b->adpc, asize); + avio_skip(s->pb, size - asize); + } + break; + case MKTAG('D','A','T','A'): + if ((start < avio_tell(s->pb)) || + (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_skip(s->pb, start - avio_tell(s->pb)); + + if (major != 1 || minor) + avpriv_request_sample(s, "Version %d.%d", major, minor); + + return 0; + default: + av_log(s, AV_LOG_WARNING, "skipping unknown chunk: %X\n", chunk); +skip: + avio_skip(s->pb, size); + } + } + +fail: + read_close(s); + + return ret; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecContext *codec = s->streams[0]->codec; + BRSTMDemuxContext *b = s->priv_data; + uint32_t samples, size; + int ret; + + if (url_feof(s->pb)) + return AVERROR_EOF; + b->current_block++; + if (b->current_block == b->block_count) { + size = b->last_block_used_bytes; + samples = size / (8 * codec->channels) * 14; + } else if (b->current_block < b->block_count) { + size = b->block_size; + samples = b->samples_per_block; + } else { + return AVERROR_EOF; + } + + if (codec->codec_id == AV_CODEC_ID_ADPCM_THP) { + uint8_t *dst; + + if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0) + return AVERROR(ENOMEM); + dst = pkt->data; + bytestream_put_be32(&dst, size); + bytestream_put_be32(&dst, samples); + bytestream_put_buffer(&dst, b->table, 32 * codec->channels); + bytestream_put_buffer(&dst, b->adpc + 4 * codec->channels * + (b->current_block - 1), 4 * codec->channels); + + ret = avio_read(s->pb, dst, size); + if (ret != size) + av_free_packet(pkt); + pkt->duration = samples; + } else { + ret = av_get_packet(s->pb, pkt, size); + } + + pkt->stream_index = 0; + + if (ret != size) + ret = AVERROR(EIO); + + return ret; +} + +AVInputFormat ff_brstm_demuxer = { + .name = "brstm", + .long_name = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"), + .priv_data_size = sizeof(BRSTMDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, + .extensions = "brstm", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/brstm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/brstm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/brstm.o libavformat/brstm.o: libavformat/brstm.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavcodec/bytestream.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/brstm.o Binary file ffmpeg/libavformat/brstm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/c93.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/c93.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,202 @@ +/* + * Interplay C93 demuxer + * Copyright (c) 2007 Anssi Hannula + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "voc.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + uint16_t index; + uint8_t length; + uint8_t frames; +} C93BlockRecord; + +typedef struct { + VocDecContext voc; + + C93BlockRecord block_records[512]; + int current_block; + + uint32_t frame_offsets[32]; + int current_frame; + int next_pkt_is_audio; + + AVStream *audio; +} C93DemuxContext; + +static int probe(AVProbeData *p) +{ + int i; + int index = 1; + if (p->buf_size < 16) + return 0; + for (i = 0; i < 16; i += 4) { + if (AV_RL16(p->buf + i) != index || !p->buf[i + 2] || !p->buf[i + 3]) + return 0; + index += p->buf[i + 2]; + } + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + AVStream *video; + AVIOContext *pb = s->pb; + C93DemuxContext *c93 = s->priv_data; + int i; + int framecount = 0; + + for (i = 0; i < 512; i++) { + c93->block_records[i].index = avio_rl16(pb); + c93->block_records[i].length = avio_r8(pb); + c93->block_records[i].frames = avio_r8(pb); + if (c93->block_records[i].frames > 32) { + av_log(s, AV_LOG_ERROR, "too many frames in block\n"); + return AVERROR_INVALIDDATA; + } + framecount += c93->block_records[i].frames; + } + + /* Audio streams are added if audio packets are found */ + s->ctx_flags |= AVFMTCTX_NOHEADER; + + video = avformat_new_stream(s, NULL); + if (!video) + return AVERROR(ENOMEM); + + video->codec->codec_type = AVMEDIA_TYPE_VIDEO; + video->codec->codec_id = AV_CODEC_ID_C93; + video->codec->width = 320; + video->codec->height = 192; + /* 4:3 320x200 with 8 empty lines */ + video->sample_aspect_ratio = (AVRational) { 5, 6 }; + avpriv_set_pts_info(video, 64, 2, 25); + video->nb_frames = framecount; + video->duration = framecount; + video->start_time = 0; + + c93->current_block = 0; + c93->current_frame = 0; + c93->next_pkt_is_audio = 0; + return 0; +} + +#define C93_HAS_PALETTE 0x01 +#define C93_FIRST_FRAME 0x02 + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + C93DemuxContext *c93 = s->priv_data; + C93BlockRecord *br = &c93->block_records[c93->current_block]; + int datasize; + int ret, i; + + if (c93->next_pkt_is_audio) { + c93->current_frame++; + c93->next_pkt_is_audio = 0; + datasize = avio_rl16(pb); + if (datasize > 42) { + if (!c93->audio) { + c93->audio = avformat_new_stream(s, NULL); + if (!c93->audio) + return AVERROR(ENOMEM); + c93->audio->codec->codec_type = AVMEDIA_TYPE_AUDIO; + } + avio_skip(pb, 26); /* VOC header */ + ret = ff_voc_get_packet(s, pkt, c93->audio, datasize - 26); + if (ret > 0) { + pkt->stream_index = 1; + pkt->flags |= AV_PKT_FLAG_KEY; + return ret; + } + } + } + if (c93->current_frame >= br->frames) { + if (c93->current_block >= 511 || !br[1].length) + return AVERROR_EOF; + br++; + c93->current_block++; + c93->current_frame = 0; + } + + if (c93->current_frame == 0) { + avio_seek(pb, br->index * 2048, SEEK_SET); + for (i = 0; i < 32; i++) { + c93->frame_offsets[i] = avio_rl32(pb); + } + } + + avio_seek(pb,br->index * 2048 + + c93->frame_offsets[c93->current_frame], SEEK_SET); + datasize = avio_rl16(pb); /* video frame size */ + + ret = av_new_packet(pkt, datasize + 768 + 1); + if (ret < 0) + return ret; + pkt->data[0] = 0; + pkt->size = datasize + 1; + + ret = avio_read(pb, pkt->data + 1, datasize); + if (ret < datasize) { + ret = AVERROR(EIO); + goto fail; + } + + datasize = avio_rl16(pb); /* palette size */ + if (datasize) { + if (datasize != 768) { + av_log(s, AV_LOG_ERROR, "invalid palette size %u\n", datasize); + ret = AVERROR_INVALIDDATA; + goto fail; + } + pkt->data[0] |= C93_HAS_PALETTE; + ret = avio_read(pb, pkt->data + pkt->size, datasize); + if (ret < datasize) { + ret = AVERROR(EIO); + goto fail; + } + pkt->size += 768; + } + pkt->stream_index = 0; + c93->next_pkt_is_audio = 1; + + /* only the first frame is guaranteed to not reference previous frames */ + if (c93->current_block == 0 && c93->current_frame == 0) { + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->data[0] |= C93_FIRST_FRAME; + } + return 0; + + fail: + av_free_packet(pkt); + return ret; +} + +AVInputFormat ff_c93_demuxer = { + .name = "c93", + .long_name = NULL_IF_CONFIG_SMALL("Interplay C93"), + .priv_data_size = sizeof(C93DemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/c93.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/c93.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/c93.o libavformat/c93.o: libavformat/c93.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/voc.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/c93.o Binary file ffmpeg/libavformat/c93.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cache.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cache.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,140 @@ +/* + * Input cache protocol. + * Copyright (c) 2011 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Based on file.c by Fabrice Bellard + */ + +/** + * @TODO + * support non continuous caching + * support keeping files + * support filling with a background thread + */ + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/file.h" +#include "avformat.h" +#include +#if HAVE_IO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include "os_support.h" +#include "url.h" + +typedef struct Context { + int fd; + int64_t end; + int64_t pos; + URLContext *inner; +} Context; + +static int cache_open(URLContext *h, const char *arg, int flags) +{ + char *buffername; + Context *c= h->priv_data; + + av_strstart(arg, "cache:", &arg); + + c->fd = av_tempfile("ffcache", &buffername, 0, h); + if (c->fd < 0){ + av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n"); + return c->fd; + } + + unlink(buffername); + av_freep(&buffername); + + return ffurl_open(&c->inner, arg, flags, &h->interrupt_callback, NULL); +} + +static int cache_read(URLContext *h, unsigned char *buf, int size) +{ + Context *c= h->priv_data; + int r; + + if(c->posend){ + r = read(c->fd, buf, FFMIN(size, c->end - c->pos)); + if(r>0) + c->pos += r; + return (-1 == r)?AVERROR(errno):r; + }else{ + r = ffurl_read(c->inner, buf, size); + if(r > 0){ + int r2= write(c->fd, buf, r); + av_assert0(r2==r); // FIXME handle cache failure + c->pos += r; + c->end += r; + } + return r; + } +} + +static int64_t cache_seek(URLContext *h, int64_t pos, int whence) +{ + Context *c= h->priv_data; + + if (whence == AVSEEK_SIZE) { + pos= ffurl_seek(c->inner, pos, whence); + if(pos <= 0){ + pos= ffurl_seek(c->inner, -1, SEEK_END); + ffurl_seek(c->inner, c->end, SEEK_SET); + if(pos <= 0) + return c->end; + } + return pos; + } + + pos= lseek(c->fd, pos, whence); + if(pos<0){ + return pos; + }else if(pos <= c->end){ + c->pos= pos; + return pos; + }else{ + if(lseek(c->fd, c->pos, SEEK_SET) < 0) { + av_log(h, AV_LOG_ERROR, "Failure to seek in cache\n"); + } + return AVERROR(EPIPE); + } +} + +static int cache_close(URLContext *h) +{ + Context *c= h->priv_data; + close(c->fd); + ffurl_close(c->inner); + + return 0; +} + +URLProtocol ff_cache_protocol = { + .name = "cache", + .url_open = cache_open, + .url_read = cache_read, + .url_seek = cache_seek, + .url_close = cache_close, + .priv_data_size = sizeof(Context), +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cache.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cache.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/cache.o libavformat/cache.o: libavformat/cache.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avstring.h \ + libavutil/file.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/os_support.h config.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cache.o Binary file ffmpeg/libavformat/cache.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/caf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/caf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,66 @@ +/* + * CAF common code + * Copyright (c) 2007 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * CAF common code + */ + +#include "avformat.h" +#include "internal.h" +#include "caf.h" + +/** + * Known codec tags for CAF + */ +const AVCodecTag ff_codec_caf_tags[] = { + { AV_CODEC_ID_AAC, MKTAG('a','a','c',' ') }, + { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') }, + { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i','m','a','4') }, + { AV_CODEC_ID_ADPCM_IMA_WAV, MKTAG('m','s', 0, 17 ) }, + { AV_CODEC_ID_ADPCM_MS, MKTAG('m','s', 0, 2 ) }, + { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') }, + { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') }, + /* FIXME: use DV demuxer, as done in MOV */ + /*{ AV_CODEC_ID_DVAUDIO, MKTAG('v','d','v','a') },*/ + /*{ AV_CODEC_ID_DVAUDIO, MKTAG('d','v','c','a') },*/ + { AV_CODEC_ID_GSM, MKTAG('a','g','s','m') }, + { AV_CODEC_ID_GSM_MS, MKTAG('m','s', 0, '1') }, + { AV_CODEC_ID_ILBC, MKTAG('i','l','b','c') }, + { AV_CODEC_ID_MACE3, MKTAG('M','A','C','3') }, + { AV_CODEC_ID_MACE6, MKTAG('M','A','C','6') }, + { AV_CODEC_ID_MP1, MKTAG('.','m','p','1') }, + { AV_CODEC_ID_MP2, MKTAG('.','m','p','2') }, + { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') }, + { AV_CODEC_ID_MP3, MKTAG('m','s', 0 ,'U') }, + { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('u','l','a','w') }, + { AV_CODEC_ID_QCELP, MKTAG('Q','c','l','p') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','2') }, + { AV_CODEC_ID_QDM2, MKTAG('Q','D','M','C') }, + /* currently unsupported codecs */ + /*{ AC-3 over S/PDIF MKTAG('c','a','c','3') },*/ + /*{ MPEG4CELP MKTAG('c','e','l','p') },*/ + /*{ MPEG4HVXC MKTAG('h','v','x','c') },*/ + /*{ MPEG4TwinVQ MKTAG('t','w','v','q') },*/ + { AV_CODEC_ID_NONE, 0 }, +}; + diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/caf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/caf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/caf.o libavformat/caf.o: libavformat/caf.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/caf.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/caf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/caf.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,34 @@ +/* + * CAF common code + * Copyright (c) 2007 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * CAF common code + */ + +#ifndef AVFORMAT_CAF_H +#define AVFORMAT_CAF_H + +#include "internal.h" + +extern const AVCodecTag ff_codec_caf_tags[]; + +#endif /* AVFORMAT_CAF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/caf.o Binary file ffmpeg/libavformat/caf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cafdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,434 @@ +/* + * Core Audio Format demuxer + * Copyright (c) 2007 Justin Ruggles + * Copyright (c) 2009 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Core Audio Format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "isom.h" +#include "mov_chan.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/dict.h" +#include "caf.h" + +typedef struct { + int bytes_per_packet; ///< bytes in a packet, or 0 if variable + int frames_per_packet; ///< frames in a packet, or 0 if variable + int64_t num_bytes; ///< total number of bytes in stream + + int64_t packet_cnt; ///< packet counter + int64_t frame_cnt; ///< frame counter + + int64_t data_start; ///< data start position, in bytes + int64_t data_size; ///< raw data size, in bytes +} CaffContext; + +static int probe(AVProbeData *p) +{ + if (AV_RB32(p->buf) == MKBETAG('c','a','f','f') && AV_RB16(&p->buf[4]) == 1) + return AVPROBE_SCORE_MAX; + return 0; +} + +/** Read audio description chunk */ +static int read_desc_chunk(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + CaffContext *caf = s->priv_data; + AVStream *st; + int flags; + + /* new audio stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + /* parse format description */ + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->sample_rate = av_int2double(avio_rb64(pb)); + st->codec->codec_tag = avio_rl32(pb); + flags = avio_rb32(pb); + caf->bytes_per_packet = avio_rb32(pb); + st->codec->block_align = caf->bytes_per_packet; + caf->frames_per_packet = avio_rb32(pb); + st->codec->channels = avio_rb32(pb); + st->codec->bits_per_coded_sample = avio_rb32(pb); + + /* calculate bit rate for constant size packets */ + if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) { + st->codec->bit_rate = (uint64_t)st->codec->sample_rate * (uint64_t)caf->bytes_per_packet * 8 + / (uint64_t)caf->frames_per_packet; + } else { + st->codec->bit_rate = 0; + } + + /* determine codec */ + if (st->codec->codec_tag == MKTAG('l','p','c','m')) + st->codec->codec_id = ff_mov_get_lpcm_codec_id(st->codec->bits_per_coded_sample, (flags ^ 0x2) | 0x4); + else + st->codec->codec_id = ff_codec_get_id(ff_codec_caf_tags, st->codec->codec_tag); + return 0; +} + +/** Read magic cookie chunk */ +static int read_kuki_chunk(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + + if (size < 0 || size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE) + return -1; + + if (st->codec->codec_id == AV_CODEC_ID_AAC) { + /* The magic cookie format for AAC is an mp4 esds atom. + The lavc AAC decoder requires the data from the codec specific + description as extradata input. */ + int strt, skip; + MOVAtom atom; + + strt = avio_tell(pb); + ff_mov_read_esds(s, pb, atom); + skip = size - (avio_tell(pb) - strt); + if (skip < 0 || !st->codec->extradata || + st->codec->codec_id != AV_CODEC_ID_AAC) { + av_log(s, AV_LOG_ERROR, "invalid AAC magic cookie\n"); + return AVERROR_INVALIDDATA; + } + avio_skip(pb, skip); + } else if (st->codec->codec_id == AV_CODEC_ID_ALAC) { +#define ALAC_PREAMBLE 12 +#define ALAC_HEADER 36 +#define ALAC_NEW_KUKI 24 + uint8_t preamble[12]; + if (size < ALAC_NEW_KUKI) { + av_log(s, AV_LOG_ERROR, "invalid ALAC magic cookie\n"); + avio_skip(pb, size); + return AVERROR_INVALIDDATA; + } + avio_read(pb, preamble, ALAC_PREAMBLE); + + st->codec->extradata = av_mallocz(ALAC_HEADER + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + + /* For the old style cookie, we skip 12 bytes, then read 36 bytes. + * The new style cookie only contains the last 24 bytes of what was + * 36 bytes in the old style cookie, so we fabricate the first 12 bytes + * in that case to maintain compatibility. */ + if (!memcmp(&preamble[4], "frmaalac", 8)) { + if (size < ALAC_PREAMBLE + ALAC_HEADER) { + av_log(s, AV_LOG_ERROR, "invalid ALAC magic cookie\n"); + av_freep(&st->codec->extradata); + return AVERROR_INVALIDDATA; + } + avio_read(pb, st->codec->extradata, ALAC_HEADER); + avio_skip(pb, size - ALAC_PREAMBLE - ALAC_HEADER); + } else { + AV_WB32(st->codec->extradata, 36); + memcpy(&st->codec->extradata[4], "alac", 4); + AV_WB32(&st->codec->extradata[8], 0); + memcpy(&st->codec->extradata[12], preamble, 12); + avio_read(pb, &st->codec->extradata[24], ALAC_NEW_KUKI - 12); + avio_skip(pb, size - ALAC_NEW_KUKI); + } + st->codec->extradata_size = ALAC_HEADER; + } else { + st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + avio_read(pb, st->codec->extradata, size); + st->codec->extradata_size = size; + } + + return 0; +} + +/** Read packet table chunk */ +static int read_pakt_chunk(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + CaffContext *caf = s->priv_data; + int64_t pos = 0, ccount, num_packets; + int i; + + ccount = avio_tell(pb); + + num_packets = avio_rb64(pb); + if (num_packets < 0 || INT32_MAX / sizeof(AVIndexEntry) < num_packets) + return AVERROR_INVALIDDATA; + + st->nb_frames = avio_rb64(pb); /* valid frames */ + st->nb_frames += avio_rb32(pb); /* priming frames */ + st->nb_frames += avio_rb32(pb); /* remainder frames */ + + st->duration = 0; + for (i = 0; i < num_packets; i++) { + av_add_index_entry(s->streams[0], pos, st->duration, 0, 0, AVINDEX_KEYFRAME); + pos += caf->bytes_per_packet ? caf->bytes_per_packet : ff_mp4_read_descr_len(pb); + st->duration += caf->frames_per_packet ? caf->frames_per_packet : ff_mp4_read_descr_len(pb); + } + + if (avio_tell(pb) - ccount > size) { + av_log(s, AV_LOG_ERROR, "error reading packet table\n"); + return AVERROR_INVALIDDATA; + } + avio_skip(pb, ccount + size - avio_tell(pb)); + + caf->num_bytes = pos; + return 0; +} + +/** Read information chunk */ +static void read_info_chunk(AVFormatContext *s, int64_t size) +{ + AVIOContext *pb = s->pb; + unsigned int i; + unsigned int nb_entries = avio_rb32(pb); + for (i = 0; i < nb_entries; i++) { + char key[32]; + char value[1024]; + avio_get_str(pb, INT_MAX, key, sizeof(key)); + avio_get_str(pb, INT_MAX, value, sizeof(value)); + av_dict_set(&s->metadata, key, value, 0); + } +} + +static int read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + CaffContext *caf = s->priv_data; + AVStream *st; + uint32_t tag = 0; + int found_data, ret; + int64_t size, pos; + + avio_skip(pb, 8); /* magic, version, file flags */ + + /* audio description chunk */ + if (avio_rb32(pb) != MKBETAG('d','e','s','c')) { + av_log(s, AV_LOG_ERROR, "desc chunk not present\n"); + return AVERROR_INVALIDDATA; + } + size = avio_rb64(pb); + if (size != 32) + return AVERROR_INVALIDDATA; + + ret = read_desc_chunk(s); + if (ret) + return ret; + st = s->streams[0]; + + /* parse each chunk */ + found_data = 0; + while (!url_feof(pb)) { + + /* stop at data chunk if seeking is not supported or + data chunk size is unknown */ + if (found_data && (caf->data_size < 0 || !pb->seekable)) + break; + + tag = avio_rb32(pb); + size = avio_rb64(pb); + pos = avio_tell(pb); + if (url_feof(pb)) + break; + + switch (tag) { + case MKBETAG('d','a','t','a'): + avio_skip(pb, 4); /* edit count */ + caf->data_start = avio_tell(pb); + caf->data_size = size < 0 ? -1 : size - 4; + if (caf->data_size > 0 && pb->seekable) + avio_skip(pb, caf->data_size); + found_data = 1; + break; + + case MKBETAG('c','h','a','n'): + if ((ret = ff_mov_read_chan(s, s->pb, st, size)) < 0) + return ret; + break; + + /* magic cookie chunk */ + case MKBETAG('k','u','k','i'): + if (read_kuki_chunk(s, size)) + return AVERROR_INVALIDDATA; + break; + + /* packet table chunk */ + case MKBETAG('p','a','k','t'): + if (read_pakt_chunk(s, size)) + return AVERROR_INVALIDDATA; + break; + + case MKBETAG('i','n','f','o'): + read_info_chunk(s, size); + break; + + default: +#define _(x) ((x) >= ' ' ? (x) : ' ') + av_log(s, AV_LOG_WARNING, "skipping CAF chunk: %08X (%c%c%c%c), size %"PRId64"\n", + tag, _(tag>>24), _((tag>>16)&0xFF), _((tag>>8)&0xFF), _(tag&0xFF), size); +#undef _ + case MKBETAG('f','r','e','e'): + if (size < 0) + return AVERROR_INVALIDDATA; + break; + } + + if (size > 0) { + if (pos > INT64_MAX - size) + return AVERROR_INVALIDDATA; + avio_skip(pb, FFMAX(0, pos + size - avio_tell(pb))); + } + } + + if (!found_data) + return AVERROR_INVALIDDATA; + + if (caf->bytes_per_packet > 0 && caf->frames_per_packet > 0) { + if (caf->data_size > 0) + st->nb_frames = (caf->data_size / caf->bytes_per_packet) * caf->frames_per_packet; + } else if (st->nb_index_entries && st->duration > 0) { + st->codec->bit_rate = st->codec->sample_rate * caf->data_size * 8 / + st->duration; + } else { + av_log(s, AV_LOG_ERROR, "Missing packet table. It is required when " + "block size or frame size are variable.\n"); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + st->start_time = 0; + + /* position the stream at the start of data */ + if (caf->data_size >= 0) + avio_seek(pb, caf->data_start, SEEK_SET); + + return 0; +} + +#define CAF_MAX_PKT_SIZE 4096 + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + CaffContext *caf = s->priv_data; + int res, pkt_size = 0, pkt_frames = 0; + int64_t left = CAF_MAX_PKT_SIZE; + + if (url_feof(pb)) + return AVERROR_EOF; + + /* don't read past end of data chunk */ + if (caf->data_size > 0) { + left = (caf->data_start + caf->data_size) - avio_tell(pb); + if (!left) + return AVERROR_EOF; + if (left < 0) + return AVERROR(EIO); + } + + pkt_frames = caf->frames_per_packet; + pkt_size = caf->bytes_per_packet; + + if (pkt_size > 0 && pkt_frames == 1) { + pkt_size = (CAF_MAX_PKT_SIZE / pkt_size) * pkt_size; + pkt_size = FFMIN(pkt_size, left); + pkt_frames = pkt_size / caf->bytes_per_packet; + } else if (st->nb_index_entries) { + if (caf->packet_cnt < st->nb_index_entries - 1) { + pkt_size = st->index_entries[caf->packet_cnt + 1].pos - st->index_entries[caf->packet_cnt].pos; + pkt_frames = st->index_entries[caf->packet_cnt + 1].timestamp - st->index_entries[caf->packet_cnt].timestamp; + } else if (caf->packet_cnt == st->nb_index_entries - 1) { + pkt_size = caf->num_bytes - st->index_entries[caf->packet_cnt].pos; + pkt_frames = st->duration - st->index_entries[caf->packet_cnt].timestamp; + } else { + return AVERROR(EIO); + } + } + + if (pkt_size == 0 || pkt_frames == 0 || pkt_size > left) + return AVERROR(EIO); + + res = av_get_packet(pb, pkt, pkt_size); + if (res < 0) + return res; + + pkt->size = res; + pkt->stream_index = 0; + pkt->dts = pkt->pts = caf->frame_cnt; + + caf->packet_cnt++; + caf->frame_cnt += pkt_frames; + + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + AVStream *st = s->streams[0]; + CaffContext *caf = s->priv_data; + int64_t pos, packet_cnt, frame_cnt; + + timestamp = FFMAX(timestamp, 0); + + if (caf->frames_per_packet > 0 && caf->bytes_per_packet > 0) { + /* calculate new byte position based on target frame position */ + pos = caf->bytes_per_packet * (timestamp / caf->frames_per_packet); + if (caf->data_size > 0) + pos = FFMIN(pos, caf->data_size); + packet_cnt = pos / caf->bytes_per_packet; + frame_cnt = caf->frames_per_packet * packet_cnt; + } else if (st->nb_index_entries) { + packet_cnt = av_index_search_timestamp(st, timestamp, flags); + frame_cnt = st->index_entries[packet_cnt].timestamp; + pos = st->index_entries[packet_cnt].pos; + } else { + return -1; + } + + if (avio_seek(s->pb, pos + caf->data_start, SEEK_SET) < 0) + return -1; + + caf->packet_cnt = packet_cnt; + caf->frame_cnt = frame_cnt; + + return 0; +} + +AVInputFormat ff_caf_demuxer = { + .name = "caf", + .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), + .priv_data_size = sizeof(CaffContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .read_seek = read_seek, + .codec_tag = (const AVCodecTag* const []){ ff_codec_caf_tags, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cafdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/cafdec.o libavformat/cafdec.o: libavformat/cafdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/isom.h libavformat/dv.h libavformat/mov_chan.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/intfloat.h libavformat/caf.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafdec.o Binary file ffmpeg/libavformat/cafdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cafenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,279 @@ +/* + * Core Audio Format muxer + * Copyright (c) 2011 Carl Eugen Hoyos + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "caf.h" +#include "isom.h" +#include "avio_internal.h" +#include "libavutil/intfloat.h" +#include "libavutil/dict.h" + +typedef struct { + int64_t data; + uint8_t *pkt_sizes; + int size_buffer_size; + int size_entries_used; + int packets; +} CAFContext; + +static uint32_t codec_flags(enum AVCodecID codec_id) { + switch (codec_id) { + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64BE: + return 1; //< kCAFLinearPCMFormatFlagIsFloat + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S32LE: + return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F64LE: + return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian + default: + return 0; + } +} + +static uint32_t samples_per_packet(enum AVCodecID codec_id, int channels) { + switch (codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + return 1; + case AV_CODEC_ID_MACE3: + case AV_CODEC_ID_MACE6: + return 6; + case AV_CODEC_ID_ADPCM_IMA_QT: + return 64; + case AV_CODEC_ID_AMR_NB: + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_ILBC: + case AV_CODEC_ID_QCELP: + return 160; + case AV_CODEC_ID_GSM_MS: + return 320; + case AV_CODEC_ID_MP1: + return 384; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + return 1152; + case AV_CODEC_ID_AC3: + return 1536; + case AV_CODEC_ID_ALAC: + case AV_CODEC_ID_QDM2: + return 4096; + case AV_CODEC_ID_ADPCM_IMA_WAV: + return (1024 - 4 * channels) * 8 / (4 * channels) + 1; + case AV_CODEC_ID_ADPCM_MS: + return (1024 - 7 * channels) * 2 / channels + 2; + default: + return 0; + } +} + +static int caf_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + CAFContext *caf = s->priv_data; + AVDictionaryEntry *t = NULL; + unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, enc->codec_id); + int64_t chunk_size = 0; + + switch (enc->codec_id) { + case AV_CODEC_ID_AAC: + case AV_CODEC_ID_AC3: + av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n"); + return AVERROR_PATCHWELCOME; + } + + switch (enc->codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64LE: + case AV_CODEC_ID_PCM_F64BE: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_PCM_MULAW: + codec_tag = MKTAG('l','p','c','m'); + } + + if (!codec_tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR_INVALIDDATA; + } + + if (!enc->block_align && !pb->seekable) { + av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n"); + return AVERROR_INVALIDDATA; + } + + ffio_wfourcc(pb, "caff"); //< mFileType + avio_wb16(pb, 1); //< mFileVersion + avio_wb16(pb, 0); //< mFileFlags + + ffio_wfourcc(pb, "desc"); //< Audio Description chunk + avio_wb64(pb, 32); //< mChunkSize + avio_wb64(pb, av_double2int(enc->sample_rate)); //< mSampleRate + avio_wl32(pb, codec_tag); //< mFormatID + avio_wb32(pb, codec_flags(enc->codec_id)); //< mFormatFlags + avio_wb32(pb, enc->block_align); //< mBytesPerPacket + avio_wb32(pb, samples_per_packet(enc->codec_id, enc->channels)); //< mFramesPerPacket + avio_wb32(pb, enc->channels); //< mChannelsPerFrame + avio_wb32(pb, av_get_bits_per_sample(enc->codec_id)); //< mBitsPerChannel + + if (enc->channel_layout) { + ffio_wfourcc(pb, "chan"); + avio_wb64(pb, 12); + ff_mov_write_chan(pb, enc->channel_layout); + } + + if (enc->codec_id == AV_CODEC_ID_ALAC) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 12 + enc->extradata_size); + avio_write(pb, "\0\0\0\14frmaalac", 12); + avio_write(pb, enc->extradata, enc->extradata_size); + } else if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, 29); + avio_write(pb, "\0\0\0\14frmasamr", 12); + avio_wb32(pb, 0x11); /* size */ + avio_write(pb, "samrFFMP", 8); + avio_w8(pb, 0); /* decoder version */ + + avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ + avio_w8(pb, 0x00); /* Mode change period (no restriction) */ + avio_w8(pb, 0x01); /* Frames per sample */ + } else if (enc->codec_id == AV_CODEC_ID_QDM2) { + ffio_wfourcc(pb, "kuki"); + avio_wb64(pb, enc->extradata_size); + avio_write(pb, enc->extradata, enc->extradata_size); + } + + if (av_dict_count(s->metadata)) { + ffio_wfourcc(pb, "info"); //< Information chunk + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + chunk_size += strlen(t->key) + strlen(t->value) + 2; + } + avio_wb64(pb, chunk_size + 4); + avio_wb32(pb, av_dict_count(s->metadata)); + t = NULL; + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + avio_put_str(pb, t->key); + avio_put_str(pb, t->value); + } + } + + ffio_wfourcc(pb, "data"); //< Audio Data chunk + caf->data = avio_tell(pb); + avio_wb64(pb, -1); //< mChunkSize + avio_wb32(pb, 0); //< mEditCount + + avio_flush(pb); + return 0; +} + +static int caf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + CAFContext *caf = s->priv_data; + + avio_write(s->pb, pkt->data, pkt->size); + if (!s->streams[0]->codec->block_align) { + void *pkt_sizes = caf->pkt_sizes; + int i, alloc_size = caf->size_entries_used + 5; + if (alloc_size < 0) { + caf->pkt_sizes = NULL; + } else { + caf->pkt_sizes = av_fast_realloc(caf->pkt_sizes, + &caf->size_buffer_size, + alloc_size); + } + if (!caf->pkt_sizes) { + av_free(pkt_sizes); + return AVERROR(ENOMEM); + } + for (i = 4; i > 0; i--) { + unsigned top = pkt->size >> i * 7; + if (top) + caf->pkt_sizes[caf->size_entries_used++] = 128 | top; + } + caf->pkt_sizes[caf->size_entries_used++] = pkt->size & 127; + caf->packets++; + } + return 0; +} + +static int caf_write_trailer(AVFormatContext *s) +{ + CAFContext *caf = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[0]->codec; + + if (pb->seekable) { + int64_t file_size = avio_tell(pb); + + avio_seek(pb, caf->data, SEEK_SET); + avio_wb64(pb, file_size - caf->data - 8); + avio_seek(pb, file_size, SEEK_SET); + if (!enc->block_align) { + ffio_wfourcc(pb, "pakt"); + avio_wb64(pb, caf->size_entries_used + 24); + avio_wb64(pb, caf->packets); ///< mNumberPackets + avio_wb64(pb, caf->packets * samples_per_packet(enc->codec_id, enc->channels)); ///< mNumberValidFrames + avio_wb32(pb, 0); ///< mPrimingFrames + avio_wb32(pb, 0); ///< mRemainderFrames + avio_write(pb, caf->pkt_sizes, caf->size_entries_used); + caf->size_buffer_size = 0; + } + avio_flush(pb); + } + av_freep(&caf->pkt_sizes); + return 0; +} + +AVOutputFormat ff_caf_muxer = { + .name = "caf", + .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), + .mime_type = "audio/x-caf", + .extensions = "caf", + .priv_data_size = sizeof(CAFContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = caf_write_header, + .write_packet = caf_write_packet, + .write_trailer = caf_write_trailer, + .codec_tag = (const AVCodecTag* const []){ff_codec_caf_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cafenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/cafenc.o libavformat/cafenc.o: libavformat/cafenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/caf.h \ + libavformat/internal.h libavformat/isom.h libavformat/dv.h \ + libavformat/avio_internal.h libavformat/url.h libavutil/intfloat.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cafenc.o Binary file ffmpeg/libavformat/cafenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cavsvideodec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cavsvideodec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,68 @@ +/* + * RAW Chinese AVS video demuxer + * Copyright (c) 2009 Stefan Gehrer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +#define CAVS_SEQ_START_CODE 0x000001b0 +#define CAVS_PIC_I_START_CODE 0x000001b3 +#define CAVS_UNDEF_START_CODE 0x000001b4 +#define CAVS_PIC_PB_START_CODE 0x000001b6 +#define CAVS_VIDEO_EDIT_CODE 0x000001b7 +#define CAVS_PROFILE_JIZHUN 0x20 + +static int cavsvideo_probe(AVProbeData *p) +{ + uint32_t code= -1; + int pic=0, seq=0, slice_pos = 0; + int i; + + for(i=0; ibuf_size; i++){ + code = (code<<8) + p->buf[i]; + if ((code & 0xffffff00) == 0x100) { + if(code < CAVS_SEQ_START_CODE) { + /* slices have to be consecutive */ + if(code < slice_pos) + return 0; + slice_pos = code; + } else { + slice_pos = 0; + } + if (code == CAVS_SEQ_START_CODE) { + seq++; + /* check for the only currently supported profile */ + if(p->buf[i+1] != CAVS_PROFILE_JIZHUN) + return 0; + } else if ((code == CAVS_PIC_I_START_CODE) || + (code == CAVS_PIC_PB_START_CODE)) { + pic++; + } else if ((code == CAVS_UNDEF_START_CODE) || + (code > CAVS_VIDEO_EDIT_CODE)) { + return 0; + } + } + } + if(seq && seq*9<=pic*10) + return AVPROBE_SCORE_MAX/2; + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(cavsvideo, "raw Chinese AVS (Audio Video Standard)", cavsvideo_probe, NULL, AV_CODEC_ID_CAVS) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cavsvideodec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cavsvideodec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/cavsvideodec.o libavformat/cavsvideodec.o: \ + libavformat/cavsvideodec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cavsvideodec.o Binary file ffmpeg/libavformat/cavsvideodec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cdg.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,79 @@ +/* + * CD Graphics Demuxer + * Copyright (c) 2009 Michael Tison + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +#define CDG_PACKET_SIZE 24 +#define CDG_COMMAND 0x09 +#define CDG_MASK 0x3F + +static int read_header(AVFormatContext *s) +{ + AVStream *vst; + int ret; + + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_CDGRAPHICS; + + /// 75 sectors/sec * 4 packets/sector = 300 packets/sec + avpriv_set_pts_info(vst, 32, 1, 300); + + ret = avio_size(s->pb); + if (ret > 0) + vst->duration = (ret * vst->time_base.den) / (CDG_PACKET_SIZE * 300); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + + while (1) { + ret = av_get_packet(s->pb, pkt, CDG_PACKET_SIZE); + if (ret < 1 || (pkt->data[0] & CDG_MASK) == CDG_COMMAND) + break; + av_free_packet(pkt); + } + + pkt->stream_index = 0; + pkt->dts= + pkt->pts= pkt->pos / CDG_PACKET_SIZE; + + if(ret>5 && (pkt->data[0]&0x3F) == 9 && (pkt->data[1]&0x3F)==1 && !(pkt->data[2+2+1] & 0x0F)){ + pkt->flags = AV_PKT_FLAG_KEY; + } + return ret; +} + +AVInputFormat ff_cdg_demuxer = { + .name = "cdg", + .long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "cdg", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdg.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cdg.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/cdg.o libavformat/cdg.o: libavformat/cdg.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdg.o Binary file ffmpeg/libavformat/cdg.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdxl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cdxl.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,231 @@ +/* + * CDXL demuxer + * Copyright (c) 2011-2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/parseutils.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +#define CDXL_HEADER_SIZE 32 + +typedef struct CDXLDemuxContext { + AVClass *class; + int sample_rate; + char *framerate; + AVRational fps; + int read_chunk; + uint8_t header[CDXL_HEADER_SIZE]; + int video_stream_index; + int audio_stream_index; +} CDXLDemuxContext; + +static int cdxl_read_probe(AVProbeData *p) +{ + int score = AVPROBE_SCORE_MAX / 2 + 10; + + if (p->buf_size < CDXL_HEADER_SIZE) + return 0; + + /* reserved bytes should always be set to 0 */ + if (AV_RN64(&p->buf[24]) || AV_RN16(&p->buf[10])) + return 0; + + /* check type */ + if (p->buf[0] != 1) + return 0; + + /* check palette size */ + if (AV_RB16(&p->buf[20]) > 512) + return 0; + + /* check number of planes */ + if (p->buf[18] || !p->buf[19]) + return 0; + + /* check widh and height */ + if (!AV_RN16(&p->buf[14]) || !AV_RN16(&p->buf[16])) + return 0; + + /* chunk size */ + if (AV_RB32(&p->buf[2]) < AV_RB16(&p->buf[22]) + AV_RB16(&p->buf[20]) + CDXL_HEADER_SIZE) + return 0; + + /* previous chunk size */ + if (AV_RN32(&p->buf[6])) + score /= 2; + + /* current frame number, usually starts from 1 */ + if (AV_RB16(&p->buf[12]) != 1) + score /= 2; + + return score; +} + +static int cdxl_read_header(AVFormatContext *s) +{ + CDXLDemuxContext *cdxl = s->priv_data; + int ret; + + if (cdxl->framerate && (ret = av_parse_video_rate(&cdxl->fps, cdxl->framerate)) < 0) { + av_log(s, AV_LOG_ERROR, + "Could not parse framerate: %s.\n", cdxl->framerate); + return ret; + } + + cdxl->read_chunk = 0; + cdxl->video_stream_index = -1; + cdxl->audio_stream_index = -1; + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + return 0; +} + +static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + CDXLDemuxContext *cdxl = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t current_size, video_size, image_size; + uint16_t audio_size, palette_size, width, height; + int64_t pos; + int ret; + + if (url_feof(pb)) + return AVERROR_EOF; + + pos = avio_tell(pb); + if (!cdxl->read_chunk && + avio_read(pb, cdxl->header, CDXL_HEADER_SIZE) != CDXL_HEADER_SIZE) + return AVERROR_EOF; + if (cdxl->header[0] != 1) { + av_log(s, AV_LOG_ERROR, "non-standard cdxl file\n"); + return AVERROR_INVALIDDATA; + } + + current_size = AV_RB32(&cdxl->header[2]); + width = AV_RB16(&cdxl->header[14]); + height = AV_RB16(&cdxl->header[16]); + palette_size = AV_RB16(&cdxl->header[20]); + audio_size = AV_RB16(&cdxl->header[22]); + image_size = FFALIGN(width, 16) * height * cdxl->header[19] / 8; + video_size = palette_size + image_size; + + if (palette_size > 512) + return AVERROR_INVALIDDATA; + if (current_size < (uint64_t)audio_size + video_size + CDXL_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + if (cdxl->read_chunk && audio_size) { + if (cdxl->audio_stream_index == -1) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 0; + st->codec->codec_id = AV_CODEC_ID_PCM_S8; + if (cdxl->header[1] & 0x10) { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + st->codec->sample_rate = cdxl->sample_rate; + st->start_time = 0; + cdxl->audio_stream_index = st->index; + avpriv_set_pts_info(st, 64, 1, cdxl->sample_rate); + } + + ret = av_get_packet(pb, pkt, audio_size); + if (ret < 0) + return ret; + pkt->stream_index = cdxl->audio_stream_index; + pkt->pos = pos; + pkt->duration = audio_size; + cdxl->read_chunk = 0; + } else { + if (cdxl->video_stream_index == -1) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = 0; + st->codec->codec_id = AV_CODEC_ID_CDXL; + st->codec->width = width; + st->codec->height = height; + st->start_time = 0; + cdxl->video_stream_index = st->index; + if (cdxl->framerate) + avpriv_set_pts_info(st, 64, cdxl->fps.den, cdxl->fps.num); + else + avpriv_set_pts_info(st, 64, 1, cdxl->sample_rate); + } + + if (av_new_packet(pkt, video_size + CDXL_HEADER_SIZE) < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, cdxl->header, CDXL_HEADER_SIZE); + ret = avio_read(pb, pkt->data + CDXL_HEADER_SIZE, video_size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + av_shrink_packet(pkt, CDXL_HEADER_SIZE + ret); + pkt->stream_index = cdxl->video_stream_index; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pos = pos; + pkt->duration = cdxl->framerate ? 1 : audio_size ? audio_size : 220; + cdxl->read_chunk = audio_size; + } + + if (!cdxl->read_chunk) + avio_skip(pb, current_size - audio_size - video_size - CDXL_HEADER_SIZE); + return ret; +} + +#define OFFSET(x) offsetof(CDXLDemuxContext, x) +static const AVOption cdxl_options[] = { + { "sample_rate", "", OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 11025 }, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass cdxl_demuxer_class = { + .class_name = "CDXL demuxer", + .item_name = av_default_item_name, + .option = cdxl_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_cdxl_demuxer = { + .name = "cdxl", + .long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"), + .priv_data_size = sizeof(CDXLDemuxContext), + .read_probe = cdxl_read_probe, + .read_header = cdxl_read_header, + .read_packet = cdxl_read_packet, + .extensions = "cdxl,xl", + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &cdxl_demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdxl.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cdxl.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/cdxl.o libavformat/cdxl.o: libavformat/cdxl.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavutil/parseutils.h \ + libavutil/rational.h libavutil/opt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cdxl.o Binary file ffmpeg/libavformat/cdxl.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/concat.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,193 @@ +/* + * Concat URL protocol + * Copyright (c) 2006 Steve Lhomme + * Copyright (c) 2007 Wolfram Gloger + * Copyright (c) 2010 Michele Orrù + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "libavutil/avstring.h" +#include "libavutil/mem.h" +#include "url.h" + +#define AV_CAT_SEPARATOR "|" + +struct concat_nodes { + URLContext *uc; ///< node's URLContext + int64_t size; ///< url filesize +}; + +struct concat_data { + struct concat_nodes *nodes; ///< list of nodes to concat + size_t length; ///< number of cat'ed nodes + size_t current; ///< index of currently read node +}; + +static av_cold int concat_close(URLContext *h) +{ + int err = 0; + size_t i; + struct concat_data *data = h->priv_data; + struct concat_nodes *nodes = data->nodes; + + for (i = 0; i != data->length; i++) + err |= ffurl_close(nodes[i].uc); + + av_freep(&data->nodes); + + return err < 0 ? -1 : 0; +} + +static av_cold int concat_open(URLContext *h, const char *uri, int flags) +{ + char *node_uri = NULL, *tmp_uri; + int err = 0; + int64_t size; + size_t len, i; + URLContext *uc; + struct concat_data *data = h->priv_data; + struct concat_nodes *nodes; + + av_strstart(uri, "concat:", &uri); + + for (i = 0, len = 1; uri[i]; i++) + if (uri[i] == *AV_CAT_SEPARATOR) + /* integer overflow */ + if (++len == UINT_MAX / sizeof(*nodes)) { + av_freep(&h->priv_data); + return AVERROR(ENAMETOOLONG); + } + + if (!(nodes = av_malloc(sizeof(*nodes) * len))) { + return AVERROR(ENOMEM); + } else + data->nodes = nodes; + + /* handle input */ + if (!*uri) + err = AVERROR(ENOENT); + for (i = 0; *uri; i++) { + /* parsing uri */ + len = strcspn(uri, AV_CAT_SEPARATOR); + if (!(tmp_uri = av_realloc(node_uri, len+1))) { + err = AVERROR(ENOMEM); + break; + } else + node_uri = tmp_uri; + av_strlcpy(node_uri, uri, len+1); + uri += len + strspn(uri+len, AV_CAT_SEPARATOR); + + /* creating URLContext */ + if ((err = ffurl_open(&uc, node_uri, flags, + &h->interrupt_callback, NULL)) < 0) + break; + + /* creating size */ + if ((size = ffurl_size(uc)) < 0) { + ffurl_close(uc); + err = AVERROR(ENOSYS); + break; + } + + /* assembling */ + nodes[i].uc = uc; + nodes[i].size = size; + } + av_free(node_uri); + data->length = i; + + if (err < 0) + concat_close(h); + else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) { + concat_close(h); + err = AVERROR(ENOMEM); + } else + data->nodes = nodes; + return err; +} + +static int concat_read(URLContext *h, unsigned char *buf, int size) +{ + int result, total = 0; + struct concat_data *data = h->priv_data; + struct concat_nodes *nodes = data->nodes; + size_t i = data->current; + + while (size > 0) { + result = ffurl_read(nodes[i].uc, buf, size); + if (result < 0) + return total ? total : result; + if (!result) + if (i + 1 == data->length || + ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0) + break; + total += result; + buf += result; + size -= result; + } + data->current = i; + return total; +} + +static int64_t concat_seek(URLContext *h, int64_t pos, int whence) +{ + int64_t result; + struct concat_data *data = h->priv_data; + struct concat_nodes *nodes = data->nodes; + size_t i; + + switch (whence) { + case SEEK_END: + for (i = data->length - 1; + i && pos < -nodes[i].size; + i--) + pos += nodes[i].size; + break; + case SEEK_CUR: + /* get the absolute position */ + for (i = 0; i != data->current; i++) + pos += nodes[i].size; + pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR); + whence = SEEK_SET; + /* fall through with the absolute position */ + case SEEK_SET: + for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++) + pos -= nodes[i].size; + break; + default: + return AVERROR(EINVAL); + } + + result = ffurl_seek(nodes[i].uc, pos, whence); + if (result >= 0) { + data->current = i; + while (i) + result += nodes[--i].size; + } + return result; +} + +URLProtocol ff_concat_protocol = { + .name = "concat", + .url_open = concat_open, + .url_read = concat_read, + .url_seek = concat_seek, + .url_close = concat_close, + .priv_data_size = sizeof(struct concat_data), +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concat.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/concat.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/concat.o libavformat/concat.o: libavformat/concat.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/avstring.h \ + libavutil/mem.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concat.o Binary file ffmpeg/libavformat/concat.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concatdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/concatdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + char *url; + int64_t start_time; + int64_t duration; +} ConcatFile; + +typedef struct { + AVClass *class; + ConcatFile *files; + ConcatFile *cur_file; + unsigned nb_files; + AVFormatContext *avf; + int safe; + int seekable; +} ConcatContext; + +static int concat_probe(AVProbeData *probe) +{ + return memcmp(probe->buf, "ffconcat version 1.0", 20) ? + 0 : AVPROBE_SCORE_MAX; +} + +static char *get_keyword(uint8_t **cursor) +{ + char *ret = *cursor += strspn(*cursor, SPACE_CHARS); + *cursor += strcspn(*cursor, SPACE_CHARS); + if (**cursor) { + *((*cursor)++) = 0; + *cursor += strspn(*cursor, SPACE_CHARS); + } + return ret; +} + +static int safe_filename(const char *f) +{ + const char *start = f; + + for (; *f; f++) { + /* A-Za-z0-9_- */ + if (!((unsigned)((*f | 32) - 'a') < 26 || + (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) { + if (f == start) + return 0; + else if (*f == '/') + start = f + 1; + else if (*f != '.') + return 0; + } + } + return 1; +} + +#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0) + +static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile, + unsigned *nb_files_alloc) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *file; + char *url = NULL; + size_t url_len; + int ret; + + if (cat->safe > 0 && !safe_filename(filename)) { + av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename); + FAIL(AVERROR(EPERM)); + } + url_len = strlen(avf->filename) + strlen(filename) + 16; + if (!(url = av_malloc(url_len))) + FAIL(AVERROR(ENOMEM)); + ff_make_absolute_url(url, url_len, avf->filename, filename); + av_freep(&filename); + + if (cat->nb_files >= *nb_files_alloc) { + size_t n = FFMAX(*nb_files_alloc * 2, 16); + ConcatFile *new_files; + if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) || + !(new_files = av_realloc(cat->files, n * sizeof(*cat->files)))) + FAIL(AVERROR(ENOMEM)); + cat->files = new_files; + *nb_files_alloc = n; + } + + file = &cat->files[cat->nb_files++]; + memset(file, 0, sizeof(*file)); + *rfile = file; + + file->url = url; + file->start_time = AV_NOPTS_VALUE; + file->duration = AV_NOPTS_VALUE; + + return 0; + +fail: + av_free(url); + av_free(filename); + return ret; +} + +static int open_file(AVFormatContext *avf, unsigned fileno) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *file = &cat->files[fileno]; + int ret; + + if (cat->avf) + avformat_close_input(&cat->avf); + if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 || + (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) { + av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url); + return ret; + } + cat->cur_file = file; + if (file->start_time == AV_NOPTS_VALUE) + file->start_time = !fileno ? 0 : + cat->files[fileno - 1].start_time + + cat->files[fileno - 1].duration; + return 0; +} + +static int concat_read_close(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + unsigned i; + + if (cat->avf) + avformat_close_input(&cat->avf); + for (i = 0; i < cat->nb_files; i++) + av_freep(&cat->files[i].url); + av_freep(&cat->files); + return 0; +} + +static int concat_read_header(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + uint8_t buf[4096]; + uint8_t *cursor, *keyword; + int ret, line = 0, i; + unsigned nb_files_alloc = 0; + ConcatFile *file = NULL; + AVStream *st, *source_st; + int64_t time = 0; + + while (1) { + if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0) + break; + line++; + cursor = buf; + keyword = get_keyword(&cursor); + if (!*keyword || *keyword == '#') + continue; + + if (!strcmp(keyword, "file")) { + char *filename = av_get_token((const char **)&cursor, SPACE_CHARS); + if (!filename) { + av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line); + FAIL(AVERROR_INVALIDDATA); + } + if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0) + FAIL(ret); + } else if (!strcmp(keyword, "duration")) { + char *dur_str = get_keyword(&cursor); + int64_t dur; + if (!file) { + av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n", + line); + FAIL(AVERROR_INVALIDDATA); + } + if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) { + av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n", + line, dur_str); + FAIL(ret); + } + file->duration = dur; + } else if (!strcmp(keyword, "ffconcat")) { + char *ver_kw = get_keyword(&cursor); + char *ver_val = get_keyword(&cursor); + if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) { + av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line); + FAIL(AVERROR_INVALIDDATA); + } + if (cat->safe < 0) + cat->safe = 1; + } else { + av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n", + line, keyword); + FAIL(AVERROR_INVALIDDATA); + } + } + if (ret < 0) + FAIL(ret); + + for (i = 0; i < cat->nb_files; i++) { + if (cat->files[i].start_time == AV_NOPTS_VALUE) + cat->files[i].start_time = time; + else + time = cat->files[i].start_time; + if (cat->files[i].duration == AV_NOPTS_VALUE) + break; + time += cat->files[i].duration; + } + if (i == cat->nb_files) { + avf->duration = time; + cat->seekable = 1; + } + + if ((ret = open_file(avf, 0)) < 0) + FAIL(ret); + for (i = 0; i < cat->avf->nb_streams; i++) { + if (!(st = avformat_new_stream(avf, NULL))) + FAIL(AVERROR(ENOMEM)); + source_st = cat->avf->streams[i]; + if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0) + FAIL(ret); + st->r_frame_rate = source_st->r_frame_rate; + st->avg_frame_rate = source_st->avg_frame_rate; + st->time_base = source_st->time_base; + st->sample_aspect_ratio = source_st->sample_aspect_ratio; + } + + return 0; + +fail: + concat_read_close(avf); + return ret; +} + +static int open_next_file(AVFormatContext *avf) +{ + ConcatContext *cat = avf->priv_data; + unsigned fileno = cat->cur_file - cat->files; + + if (cat->cur_file->duration == AV_NOPTS_VALUE) + cat->cur_file->duration = cat->avf->duration; + + if (++fileno >= cat->nb_files) + return AVERROR_EOF; + return open_file(avf, fileno); +} + +static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) +{ + ConcatContext *cat = avf->priv_data; + int ret; + int64_t delta; + + while (1) { + if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF || + (ret = open_next_file(avf)) < 0) + break; + } + delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time, + AV_TIME_BASE_Q, + cat->avf->streams[pkt->stream_index]->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += delta; + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += delta; + return ret; +} + +static void rescale_interval(AVRational tb_in, AVRational tb_out, + int64_t *min_ts, int64_t *ts, int64_t *max_ts) +{ + *ts = av_rescale_q (* ts, tb_in, tb_out); + *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out, + AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX); +} + +static int try_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + int64_t t0 = cat->cur_file->start_time - cat->avf->start_time; + + ts -= t0; + min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0; + max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0; + if (stream >= 0) { + if (stream >= cat->avf->nb_streams) + return AVERROR(EIO); + rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base, + &min_ts, &ts, &max_ts); + } + return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags); +} + +static int real_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + int ret, left, right; + + if (stream >= 0) { + if (stream >= avf->nb_streams) + return AVERROR(EINVAL); + rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q, + &min_ts, &ts, &max_ts); + } + + left = 0; + right = cat->nb_files; + while (right - left > 1) { + int mid = (left + right) / 2; + if (ts < cat->files[mid].start_time) + right = mid; + else + left = mid; + } + + if ((ret = open_file(avf, left)) < 0) + return ret; + + ret = try_seek(avf, stream, min_ts, ts, max_ts, flags); + if (ret < 0 && !(flags & AVSEEK_FLAG_BACKWARD) && + left < cat->nb_files - 1 && + cat->files[left + 1].start_time < max_ts) { + if ((ret = open_file(avf, left + 1)) < 0) + return ret; + ret = try_seek(avf, stream, min_ts, ts, max_ts, flags); + } + return ret; +} + +static int concat_seek(AVFormatContext *avf, int stream, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + ConcatContext *cat = avf->priv_data; + ConcatFile *cur_file_saved = cat->cur_file; + AVFormatContext *cur_avf_saved = cat->avf; + int ret; + + if (!cat->seekable) + return AVERROR(ESPIPE); /* XXX: can we use it? */ + if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME)) + return AVERROR(ENOSYS); + cat->avf = NULL; + if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) { + if (cat->avf) + avformat_close_input(&cat->avf); + cat->avf = cur_avf_saved; + cat->cur_file = cur_file_saved; + } else { + avformat_close_input(&cur_avf_saved); + } + return ret; +} + +#define OFFSET(x) offsetof(ConcatContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "safe", "enable safe mode", + OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC }, + { NULL } +}; + +static const AVClass concat_class = { + .class_name = "concat demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +AVInputFormat ff_concat_demuxer = { + .name = "concat", + .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"), + .priv_data_size = sizeof(ConcatContext), + .read_probe = concat_probe, + .read_header = concat_read_header, + .read_packet = concat_read_packet, + .read_close = concat_read_close, + .read_seek2 = concat_seek, + .priv_class = &concat_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concatdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/concatdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/concatdec.o libavformat/concatdec.o: libavformat/concatdec.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/opt.h \ + libavutil/rational.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavutil/parseutils.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/concatdec.o Binary file ffmpeg/libavformat/concatdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crcenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/crcenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,67 @@ +/* + * CRC encoder (for codec/format testing) + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/adler32.h" +#include "avformat.h" + +typedef struct CRCState { + uint32_t crcval; +} CRCState; + +static int crc_write_header(struct AVFormatContext *s) +{ + CRCState *crc = s->priv_data; + + /* init CRC */ + crc->crcval = 1; + + return 0; +} + +static int crc_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + CRCState *crc = s->priv_data; + crc->crcval = av_adler32_update(crc->crcval, pkt->data, pkt->size); + return 0; +} + +static int crc_write_trailer(struct AVFormatContext *s) +{ + CRCState *crc = s->priv_data; + char buf[64]; + + snprintf(buf, sizeof(buf), "CRC=0x%08x\n", crc->crcval); + avio_write(s->pb, buf, strlen(buf)); + + return 0; +} + +AVOutputFormat ff_crc_muxer = { + .name = "crc", + .long_name = NULL_IF_CONFIG_SMALL("CRC testing"), + .priv_data_size = sizeof(CRCState), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = crc_write_header, + .write_packet = crc_write_packet, + .write_trailer = crc_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crcenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/crcenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/crcenc.o libavformat/crcenc.o: libavformat/crcenc.c \ + libavutil/adler32.h libavutil/attributes.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crcenc.o Binary file ffmpeg/libavformat/crcenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crypto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/crypto.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,170 @@ +/* + * Decryption protocol handler + * Copyright (c) 2011 Martin Storsjo + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "libavutil/aes.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "url.h" + +#define MAX_BUFFER_BLOCKS 150 +#define BLOCKSIZE 16 + +typedef struct { + const AVClass *class; + URLContext *hd; + uint8_t inbuffer [BLOCKSIZE*MAX_BUFFER_BLOCKS], + outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS]; + uint8_t *outptr; + int indata, indata_used, outdata; + int eof; + uint8_t *key; + int keylen; + uint8_t *iv; + int ivlen; + struct AVAES *aes; +} CryptoContext; + +#define OFFSET(x) offsetof(CryptoContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"key", "AES decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D }, + {"iv", "AES decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D }, + { NULL } +}; + +static const AVClass crypto_class = { + .class_name = "crypto", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) +{ + const char *nested_url; + int ret = 0; + CryptoContext *c = h->priv_data; + + if (!av_strstart(uri, "crypto+", &nested_url) && + !av_strstart(uri, "crypto:", &nested_url)) { + av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri); + ret = AVERROR(EINVAL); + goto err; + } + + if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "Key or IV not set\n"); + ret = AVERROR(EINVAL); + goto err; + } + if (flags & AVIO_FLAG_WRITE) { + av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n"); + ret = AVERROR(ENOSYS); + goto err; + } + if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ, + &h->interrupt_callback, options)) < 0) { + av_log(h, AV_LOG_ERROR, "Unable to open input\n"); + goto err; + } + c->aes = av_aes_alloc(); + if (!c->aes) { + ret = AVERROR(ENOMEM); + goto err; + } + + av_aes_init(c->aes, c->key, 128, 1); + + h->is_streamed = 1; + +err: + return ret; +} + +static int crypto_read(URLContext *h, uint8_t *buf, int size) +{ + CryptoContext *c = h->priv_data; + int blocks; +retry: + if (c->outdata > 0) { + size = FFMIN(size, c->outdata); + memcpy(buf, c->outptr, size); + c->outptr += size; + c->outdata -= size; + return size; + } + // We avoid using the last block until we've found EOF, + // since we'll remove PKCS7 padding at the end. So make + // sure we've got at least 2 blocks, so we can decrypt + // at least one. + while (c->indata - c->indata_used < 2*BLOCKSIZE) { + int n = ffurl_read(c->hd, c->inbuffer + c->indata, + sizeof(c->inbuffer) - c->indata); + if (n <= 0) { + c->eof = 1; + break; + } + c->indata += n; + } + blocks = (c->indata - c->indata_used) / BLOCKSIZE; + if (!blocks) + return AVERROR_EOF; + if (!c->eof) + blocks--; + av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks, + c->iv, 1); + c->outdata = BLOCKSIZE * blocks; + c->outptr = c->outbuffer; + c->indata_used += BLOCKSIZE * blocks; + if (c->indata_used >= sizeof(c->inbuffer)/2) { + memmove(c->inbuffer, c->inbuffer + c->indata_used, + c->indata - c->indata_used); + c->indata -= c->indata_used; + c->indata_used = 0; + } + if (c->eof) { + // Remove PKCS7 padding at the end + int padding = c->outbuffer[c->outdata - 1]; + c->outdata -= padding; + } + goto retry; +} + +static int crypto_close(URLContext *h) +{ + CryptoContext *c = h->priv_data; + if (c->hd) + ffurl_close(c->hd); + av_freep(&c->aes); + return 0; +} + +URLProtocol ff_crypto_protocol = { + .name = "crypto", + .url_open2 = crypto_open2, + .url_read = crypto_read, + .url_close = crypto_close, + .priv_data_size = sizeof(CryptoContext), + .priv_data_class = &crypto_class, + .flags = URL_PROTOCOL_FLAG_NESTED_SCHEME, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crypto.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/crypto.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/crypto.o libavformat/crypto.o: libavformat/crypto.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/aes.h \ + libavutil/avstring.h libavutil/opt.h libavformat/internal.h \ + libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/crypto.o Binary file ffmpeg/libavformat/crypto.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cutils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cutils.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,57 @@ +/* + * various simple utilities for libavformat + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "internal.h" + +#define ISLEAP(y) (((y) % 4 == 0) && (((y) % 100) != 0 || ((y) % 400) == 0)) +#define LEAPS_COUNT(y) ((y)/4 - (y)/100 + (y)/400) + +/* This is our own gmtime_r. It differs from its POSIX counterpart in a + couple of places, though. */ +struct tm *ff_brktimegm(time_t secs, struct tm *tm) +{ + int days, y, ny, m; + int md[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + days = secs / 86400; + secs %= 86400; + tm->tm_hour = secs / 3600; + tm->tm_min = (secs % 3600) / 60; + tm->tm_sec = secs % 60; + + /* oh well, may be someone some day will invent a formula for this stuff */ + y = 1970; /* start "guessing" */ + while (days > 365) { + ny = (y + days/366); + days -= (ny - y) * 365 + LEAPS_COUNT(ny - 1) - LEAPS_COUNT(y - 1); + y = ny; + } + if (days==365 && !ISLEAP(y)) { days=0; y++; } + md[1] = ISLEAP(y)?29:28; + for (m=0; days >= md[m]; m++) + days -= md[m]; + + tm->tm_year = y; /* unlike gmtime_r we store complete year here */ + tm->tm_mon = m+1; /* unlike gmtime_r tm_mon is from 1 to 12 */ + tm->tm_mday = days+1; + + return tm; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cutils.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/cutils.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/cutils.o libavformat/cutils.o: libavformat/cutils.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/cutils.o Binary file ffmpeg/libavformat/cutils.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/data_uri.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/data_uri.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/avstring.h" +#include "libavutil/base64.h" +#include "url.h" + +typedef struct { + const uint8_t *data; + void *tofree; + size_t size; + size_t pos; +} DataContext; + +static av_cold int data_open(URLContext *h, const char *uri, int flags) +{ + DataContext *dc = h->priv_data; + const char *data, *opt, *next; + char *ddata; + int ret, base64 = 0; + size_t in_size; + + /* data:content/type[;base64],payload */ + + av_strstart(uri, "data:", &uri); + data = strchr(uri, ','); + if (!data) { + av_log(h, AV_LOG_ERROR, "No ',' delimiter in URI\n"); + return AVERROR(EINVAL); + } + opt = uri; + while (opt < data) { + next = av_x_if_null(memchr(opt, ';', data - opt), data); + if (opt == uri) { + if (!memchr(opt, '/', next - opt)) { /* basic validity check */ + av_log(h, AV_LOG_ERROR, "Invalid content-type '%.*s'\n", + (int)(next - opt), opt); + return AVERROR(EINVAL); + } + av_log(h, AV_LOG_VERBOSE, "Content-type: %.*s\n", + (int)(next - opt), opt); + } else { + if (!av_strncasecmp(opt, "base64", next - opt)) { + base64 = 1; + } else { + av_log(h, AV_LOG_VERBOSE, "Ignoring option '%.*s'\n", + (int)(next - opt), opt); + } + } + opt = next + 1; + } + + data++; + in_size = strlen(data); + if (base64) { + size_t out_size = 3 * (in_size / 4) + 1; + + if (out_size > INT_MAX || !(ddata = av_malloc(out_size))) + return AVERROR(ENOMEM); + if ((ret = av_base64_decode(ddata, data, out_size)) < 0) { + av_free(ddata); + av_log(h, AV_LOG_ERROR, "Invalid base64 in URI\n"); + return ret; + } + dc->data = dc->tofree = ddata; + dc->size = ret; + } else { + dc->data = data; + dc->size = in_size; + } + return 0; +} + +static av_cold int data_close(URLContext *h) +{ + DataContext *dc = h->priv_data; + + av_freep(&dc->tofree); + return 0; +} + +static int data_read(URLContext *h, unsigned char *buf, int size) +{ + DataContext *dc = h->priv_data; + + if (dc->pos >= dc->size) + return AVERROR_EOF; + size = FFMIN(size, dc->size - dc->pos); + memcpy(buf, dc->data + dc->pos, size); + dc->pos += size; + return size; +} + +URLProtocol ff_data_protocol = { + .name = "data", + .url_open = data_open, + .url_close = data_close, + .url_read = data_read, + .priv_data_size = sizeof(DataContext), +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/data_uri.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/data_uri.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,12 @@ +libavformat/data_uri.o libavformat/data_uri.o: libavformat/data_uri.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/base64.h \ + libavformat/url.h libavformat/avio.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/dict.h libavutil/log.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/data_uri.o Binary file ffmpeg/libavformat/data_uri.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/daud.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/daud.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,95 @@ +/* + * D-Cinema audio demuxer + * Copyright (c) 2005 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" + +static int daud_header(AVFormatContext *s) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S24DAUD; + st->codec->codec_tag = MKTAG('d', 'a', 'u', 'd'); + st->codec->channels = 6; + st->codec->channel_layout = AV_CH_LAYOUT_5POINT1; + st->codec->sample_rate = 96000; + st->codec->bit_rate = 3 * 6 * 96000 * 8; + st->codec->block_align = 3 * 6; + st->codec->bits_per_coded_sample = 24; + return 0; +} + +static int daud_packet(AVFormatContext *s, AVPacket *pkt) { + AVIOContext *pb = s->pb; + int ret, size; + if (url_feof(pb)) + return AVERROR(EIO); + size = avio_rb16(pb); + avio_rb16(pb); // unknown + ret = av_get_packet(pb, pkt, size); + pkt->stream_index = 0; + return ret; +} + +static int daud_write_header(struct AVFormatContext *s) +{ + AVCodecContext *codec = s->streams[0]->codec; + if (codec->channels!=6 || codec->sample_rate!=96000) + return -1; + return 0; +} + +static int daud_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + if (pkt->size > 65535) { + av_log(s, AV_LOG_ERROR, + "Packet size too large for s302m. (%d > 65535)\n", pkt->size); + return -1; + } + avio_wb16(s->pb, pkt->size); + avio_wb16(s->pb, 0x8010); // unknown + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} + +#if CONFIG_DAUD_DEMUXER +AVInputFormat ff_daud_demuxer = { + .name = "daud", + .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), + .read_header = daud_header, + .read_packet = daud_packet, + .extensions = "302,daud", +}; +#endif + +#if CONFIG_DAUD_MUXER +AVOutputFormat ff_daud_muxer = { + .name = "daud", + .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), + .extensions = "302", + .audio_codec = AV_CODEC_ID_PCM_S24DAUD, + .video_codec = AV_CODEC_ID_NONE, + .write_header = daud_write_header, + .write_packet = daud_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/daud.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/daud.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/daud.o libavformat/daud.o: libavformat/daud.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/daud.o Binary file ffmpeg/libavformat/daud.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dfa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dfa.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,126 @@ +/* + * Chronomaster DFA Format Demuxer + * Copyright (c) 2011 Konstantin Shishkov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +static int dfa_probe(AVProbeData *p) +{ + if (p->buf_size < 4 || AV_RL32(p->buf) != MKTAG('D', 'F', 'I', 'A')) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int dfa_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + int frames; + int version; + uint32_t mspf; + + if (avio_rl32(pb) != MKTAG('D', 'F', 'I', 'A')) { + av_log(s, AV_LOG_ERROR, "Invalid magic for DFA\n"); + return AVERROR_INVALIDDATA; + } + + version = avio_rl16(pb); + frames = avio_rl16(pb); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DFA; + st->codec->width = avio_rl16(pb); + st->codec->height = avio_rl16(pb); + mspf = avio_rl32(pb); + if (!mspf) { + av_log(s, AV_LOG_WARNING, "Zero FPS reported, defaulting to 10\n"); + mspf = 100; + } + avpriv_set_pts_info(st, 24, mspf, 1000); + avio_skip(pb, 128 - 16); // padding + st->duration = frames; + + st->codec->extradata = av_malloc(2); + st->codec->extradata_size = 2; + AV_WL16(st->codec->extradata, version); + if (version == 0x100) + st->sample_aspect_ratio = (AVRational){2, 1}; + + return 0; +} + +static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + uint32_t frame_size; + int ret, first = 1; + + if (pb->eof_reached) + return AVERROR_EOF; + + if (av_get_packet(pb, pkt, 12) != 12) + return AVERROR(EIO); + while (!pb->eof_reached) { + if (!first) { + ret = av_append_packet(pb, pkt, 12); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + } else + first = 0; + frame_size = AV_RL32(pkt->data + pkt->size - 8); + if (frame_size > INT_MAX - 4) { + av_log(s, AV_LOG_ERROR, "Too large chunk size: %d\n", frame_size); + return AVERROR(EIO); + } + if (AV_RL32(pkt->data + pkt->size - 12) == MKTAG('E', 'O', 'F', 'R')) { + if (frame_size) { + av_log(s, AV_LOG_WARNING, "skipping %d bytes of end-of-frame marker chunk\n", + frame_size); + avio_skip(pb, frame_size); + } + return 0; + } + ret = av_append_packet(pb, pkt, frame_size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + } + + return 0; +} + +AVInputFormat ff_dfa_demuxer = { + .name = "dfa", + .long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"), + .read_probe = dfa_probe, + .read_header = dfa_read_header, + .read_packet = dfa_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dfa.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dfa.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/dfa.o libavformat/dfa.o: libavformat/dfa.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dfa.o Binary file ffmpeg/libavformat/dfa.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/diracdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/diracdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,34 @@ +/* + * RAW Dirac demuxer + * Copyright (c) 2007 Marco Gerards + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "rawdec.h" + +static int dirac_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('B', 'B', 'C', 'D')) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(dirac, "raw Dirac", dirac_probe, NULL, AV_CODEC_ID_DIRAC) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/diracdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/diracdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/diracdec.o libavformat/diracdec.o: libavformat/diracdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/diracdec.o Binary file ffmpeg/libavformat/diracdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dnxhddec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dnxhddec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,45 @@ +/* + * RAW DNxHD (SMPTE VC-3) demuxer + * Copyright (c) 2008 Baptiste Coudurier + * Copyright (c) 2009 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "rawdec.h" + +static int dnxhd_probe(AVProbeData *p) +{ + static const uint8_t header[] = {0x00,0x00,0x02,0x80,0x01}; + int w, h, compression_id; + if (p->buf_size < 0x2c) + return 0; + if (memcmp(p->buf, header, 5)) + return 0; + h = AV_RB16(p->buf + 0x18); + w = AV_RB16(p->buf + 0x1a); + if (!w || !h) + return 0; + compression_id = AV_RB32(p->buf + 0x28); + if (compression_id < 1235 || compression_id > 1253) + return 0; + return AVPROBE_SCORE_MAX; +} + +FF_DEF_RAWVIDEO_DEMUXER(dnxhd, "raw DNxHD (SMPTE VC-3)", dnxhd_probe, NULL, AV_CODEC_ID_DNXHD) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dnxhddec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dnxhddec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/dnxhddec.o libavformat/dnxhddec.o: libavformat/dnxhddec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dnxhddec.o Binary file ffmpeg/libavformat/dnxhddec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dsicin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dsicin.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,232 @@ +/* + * Delphine Software International CIN File Demuxer + * Copyright (c) 2006 Gregory Montoir (cyx@users.sourceforge.net) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Delphine Software International CIN file demuxer + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" + + +typedef struct CinFileHeader { + int video_frame_size; + int video_frame_width; + int video_frame_height; + int audio_frequency; + int audio_bits; + int audio_stereo; + int audio_frame_size; +} CinFileHeader; + +typedef struct CinFrameHeader { + int audio_frame_type; + int video_frame_type; + int pal_colors_count; + int audio_frame_size; + int video_frame_size; +} CinFrameHeader; + +typedef struct CinDemuxContext { + int audio_stream_index; + int video_stream_index; + CinFileHeader file_header; + int64_t audio_stream_pts; + int64_t video_stream_pts; + CinFrameHeader frame_header; + int audio_buffer_size; +} CinDemuxContext; + + +static int cin_probe(AVProbeData *p) +{ + /* header starts with this special marker */ + if (AV_RL32(&p->buf[0]) != 0x55AA0000) + return 0; + + /* for accuracy, check some header field values */ + if (AV_RL32(&p->buf[12]) != 22050 || p->buf[16] != 16 || p->buf[17] != 0) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int cin_read_file_header(CinDemuxContext *cin, AVIOContext *pb) { + CinFileHeader *hdr = &cin->file_header; + + if (avio_rl32(pb) != 0x55AA0000) + return AVERROR_INVALIDDATA; + + hdr->video_frame_size = avio_rl32(pb); + hdr->video_frame_width = avio_rl16(pb); + hdr->video_frame_height = avio_rl16(pb); + hdr->audio_frequency = avio_rl32(pb); + hdr->audio_bits = avio_r8(pb); + hdr->audio_stereo = avio_r8(pb); + hdr->audio_frame_size = avio_rl16(pb); + + if (hdr->audio_frequency != 22050 || hdr->audio_bits != 16 || hdr->audio_stereo != 0) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int cin_read_header(AVFormatContext *s) +{ + int rc; + CinDemuxContext *cin = s->priv_data; + CinFileHeader *hdr = &cin->file_header; + AVIOContext *pb = s->pb; + AVStream *st; + + rc = cin_read_file_header(cin, pb); + if (rc) + return rc; + + cin->video_stream_pts = 0; + cin->audio_stream_pts = 0; + cin->audio_buffer_size = 0; + + /* initialize the video decoder stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(st, 32, 1, 12); + cin->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DSICINVIDEO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = hdr->video_frame_width; + st->codec->height = hdr->video_frame_height; + + /* initialize the audio decoder stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(st, 32, 1, 22050); + cin->audio_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_DSICINAUDIO; + st->codec->codec_tag = 0; /* no tag */ + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 22050; + st->codec->bits_per_coded_sample = 8; + st->codec->bit_rate = st->codec->sample_rate * st->codec->bits_per_coded_sample * st->codec->channels; + + return 0; +} + +static int cin_read_frame_header(CinDemuxContext *cin, AVIOContext *pb) { + CinFrameHeader *hdr = &cin->frame_header; + + hdr->video_frame_type = avio_r8(pb); + hdr->audio_frame_type = avio_r8(pb); + hdr->pal_colors_count = avio_rl16(pb); + hdr->video_frame_size = avio_rl32(pb); + hdr->audio_frame_size = avio_rl32(pb); + + if (url_feof(pb) || pb->error) + return AVERROR(EIO); + + if (avio_rl32(pb) != 0xAA55AA55) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int cin_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + CinDemuxContext *cin = s->priv_data; + AVIOContext *pb = s->pb; + CinFrameHeader *hdr = &cin->frame_header; + int rc, palette_type, pkt_size; + int ret; + + if (cin->audio_buffer_size == 0) { + rc = cin_read_frame_header(cin, pb); + if (rc) + return rc; + + if ((int16_t)hdr->pal_colors_count < 0) { + hdr->pal_colors_count = -(int16_t)hdr->pal_colors_count; + palette_type = 1; + } else { + palette_type = 0; + } + + /* palette and video packet */ + pkt_size = (palette_type + 3) * hdr->pal_colors_count + hdr->video_frame_size; + + pkt_size = ffio_limit(pb, pkt_size); + + ret = av_new_packet(pkt, 4 + pkt_size); + if (ret < 0) + return ret; + + pkt->stream_index = cin->video_stream_index; + pkt->pts = cin->video_stream_pts++; + + pkt->data[0] = palette_type; + pkt->data[1] = hdr->pal_colors_count & 0xFF; + pkt->data[2] = hdr->pal_colors_count >> 8; + pkt->data[3] = hdr->video_frame_type; + + ret = avio_read(pb, &pkt->data[4], pkt_size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + if (ret < pkt_size) + av_shrink_packet(pkt, 4 + ret); + + /* sound buffer will be processed on next read_packet() call */ + cin->audio_buffer_size = hdr->audio_frame_size; + return 0; + } + + /* audio packet */ + ret = av_get_packet(pb, pkt, cin->audio_buffer_size); + if (ret < 0) + return ret; + + pkt->stream_index = cin->audio_stream_index; + pkt->pts = cin->audio_stream_pts; + pkt->duration = cin->audio_buffer_size - (pkt->pts == 0); + cin->audio_stream_pts += pkt->duration; + cin->audio_buffer_size = 0; + return 0; +} + +AVInputFormat ff_dsicin_demuxer = { + .name = "dsicin", + .long_name = NULL_IF_CONFIG_SMALL("Delphine Software International CIN"), + .priv_data_size = sizeof(CinDemuxContext), + .read_probe = cin_probe, + .read_header = cin_read_header, + .read_packet = cin_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dsicin.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dsicin.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/dsicin.o libavformat/dsicin.o: libavformat/dsicin.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dsicin.o Binary file ffmpeg/libavformat/dsicin.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtsdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dtsdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,77 @@ +/* + * RAW DTS demuxer + * Copyright (c) 2008 Benjamin Larsson + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "rawdec.h" + +#define DCA_MARKER_14B_BE 0x1FFFE800 +#define DCA_MARKER_14B_LE 0xFF1F00E8 +#define DCA_MARKER_RAW_BE 0x7FFE8001 +#define DCA_MARKER_RAW_LE 0xFE7F0180 + +static int dts_probe(AVProbeData *p) +{ + const uint8_t *buf, *bufp; + uint32_t state = -1; + int markers[3] = {0}; + int sum, max; + + buf = p->buf; + + for(; buf < (p->buf+p->buf_size)-2; buf+=2) { + bufp = buf; + state = (state << 16) | bytestream_get_be16(&bufp); + + /* regular bitstream */ + if (state == DCA_MARKER_RAW_BE || state == DCA_MARKER_RAW_LE) + markers[0]++; + + /* 14 bits big-endian bitstream */ + if (state == DCA_MARKER_14B_BE) + if ((bytestream_get_be16(&bufp) & 0xFFF0) == 0x07F0) + markers[1]++; + + /* 14 bits little-endian bitstream */ + if (state == DCA_MARKER_14B_LE) + if ((bytestream_get_be16(&bufp) & 0xF0FF) == 0xF007) + markers[2]++; + } + sum = markers[0] + markers[1] + markers[2]; + max = markers[1] > markers[0]; + max = markers[2] > markers[max] ? 2 : max; + if (markers[max] > 3 && p->buf_size / markers[max] < 32*1024 && + markers[max] * 4 > sum * 3) + return AVPROBE_SCORE_MAX/2+1; + + return 0; +} + +AVInputFormat ff_dts_demuxer = { + .name = "dts", + .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), + .read_probe = dts_probe, + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "dts", + .raw_codec_id = AV_CODEC_ID_DTS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtsdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dtsdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/dtsdec.o libavformat/dtsdec.o: libavformat/dtsdec.c \ + libavcodec/bytestream.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtsdec.o Binary file ffmpeg/libavformat/dtsdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtshddec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dtshddec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,139 @@ +/* + * Raw DTS-HD demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "avformat.h" + +#define AUPR_HDR 0x415550522D484452 +#define AUPRINFO 0x41555052494E464F +#define BITSHVTB 0x4249545348565442 +#define BLACKOUT 0x424C41434B4F5554 +#define BRANCHPT 0x4252414E43485054 +#define BUILDVER 0x4255494C44564552 +#define CORESSMD 0x434F524553534D44 +#define DTSHDHDR 0x4454534844484452 +#define EXTSS_MD 0x45585453535f4d44 +#define FILEINFO 0x46494C45494E464F +#define NAVI_TBL 0x4E4156492D54424C +#define STRMDATA 0x5354524D44415441 +#define TIMECODE 0x54494D45434F4445 + +typedef struct DTSHDDemuxContext { + uint64_t data_end; +} DTSHDDemuxContext; + +static int dtshd_probe(AVProbeData *p) +{ + if (AV_RB64(p->buf) == DTSHDHDR) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int dtshd_read_header(AVFormatContext *s) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + AVIOContext *pb = s->pb; + uint64_t chunk_type, chunk_size; + AVStream *st; + int ret; + char *value; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_DTS; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + while (!url_feof(pb)) { + chunk_type = avio_rb64(pb); + chunk_size = avio_rb64(pb); + + if (chunk_size < 4) { + av_log(s, AV_LOG_ERROR, "chunk size too small\n"); + return AVERROR_INVALIDDATA; + } + if (chunk_size > ((uint64_t)1 << 61)) { + av_log(s, AV_LOG_ERROR, "chunk size too big\n"); + return AVERROR_INVALIDDATA; + } + + switch (chunk_type) { + case STRMDATA: + dtshd->data_end = chunk_size + avio_tell(pb); + if (dtshd->data_end <= chunk_size) + return AVERROR_INVALIDDATA; + return 0; + break; + case FILEINFO: + if (chunk_size > INT_MAX) + goto skip; + value = av_malloc(chunk_size); + if (!value) + goto skip; + avio_read(pb, value, chunk_size); + value[chunk_size - 1] = 0; + av_dict_set(&s->metadata, "fileinfo", value, + AV_DICT_DONT_STRDUP_VAL); + break; + default: +skip: + ret = avio_skip(pb, chunk_size); + if (ret < 0) + return ret; + }; + } + + return AVERROR_EOF; +} + +static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DTSHDDemuxContext *dtshd = s->priv_data; + int64_t size, left; + int ret; + + left = dtshd->data_end - avio_tell(s->pb); + size = FFMIN(left, 1024); + if (size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + + return ret; +} + +AVInputFormat ff_dtshd_demuxer = { + .name = "dtshd", + .long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"), + .priv_data_size = sizeof(DTSHDDemuxContext), + .read_probe = dtshd_probe, + .read_header = dtshd_read_header, + .read_packet = raw_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "dtshd", + .raw_codec_id = AV_CODEC_ID_DTS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtshddec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dtshddec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/dtshddec.o libavformat/dtshddec.o: libavformat/dtshddec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavutil/dict.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dtshddec.o Binary file ffmpeg/libavformat/dtshddec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dv.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,614 @@ +/* + * General DV muxer/demuxer + * Copyright (c) 2003 Roman Shaposhnik + * + * Many thanks to Dan Dennedy for providing wealth + * of DV technical info. + * + * Raw DV format + * Copyright (c) 2002 Fabrice Bellard + * + * 50 Mbps (DVCPRO50) and 100 Mbps (DVCPRO HD) support + * Copyright (c) 2006 Daniel Maas + * Funded by BBC Research & Development + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include "avformat.h" +#include "internal.h" +#include "libavcodec/dv_profile.h" +#include "libavcodec/dvdata.h" +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/timecode.h" +#include "dv.h" +#include "libavutil/avassert.h" + +struct DVDemuxContext { + const DVprofile* sys; /* Current DV profile. E.g.: 525/60, 625/50 */ + AVFormatContext* fctx; + AVStream* vst; + AVStream* ast[4]; + AVPacket audio_pkt[4]; + uint8_t audio_buf[4][8192]; + int ach; + int frames; + uint64_t abytes; +}; + +static inline uint16_t dv_audio_12to16(uint16_t sample) +{ + uint16_t shift, result; + + sample = (sample < 0x800) ? sample : sample | 0xf000; + shift = (sample & 0xf00) >> 8; + + if (shift < 0x2 || shift > 0xd) { + result = sample; + } else if (shift < 0x8) { + shift--; + result = (sample - (256 * shift)) << shift; + } else { + shift = 0xe - shift; + result = ((sample + ((256 * shift) + 1)) << shift) - 1; + } + + return result; +} + +/* + * This is the dumbest implementation of all -- it simply looks at + * a fixed offset and if pack isn't there -- fails. We might want + * to have a fallback mechanism for complete search of missing packs. + */ +static const uint8_t* dv_extract_pack(uint8_t* frame, enum dv_pack_type t) +{ + int offs; + + switch (t) { + case dv_audio_source: + offs = (80*6 + 80*16*3 + 3); + break; + case dv_audio_control: + offs = (80*6 + 80*16*4 + 3); + break; + case dv_video_control: + offs = (80*5 + 48 + 5); + break; + case dv_timecode: + offs = (80*1 + 3 + 3); + break; + default: + return NULL; + } + + return frame[offs] == t ? &frame[offs] : NULL; +} + +static const int dv_audio_frequency[3] = { + 48000, 44100, 32000, +}; + +/* + * There's a couple of assumptions being made here: + * 1. By default we silence erroneous (0x8000/16bit 0x800/12bit) audio samples. + * We can pass them upwards when libavcodec will be ready to deal with them. + * 2. We don't do software emphasis. + * 3. Audio is always returned as 16bit linear samples: 12bit nonlinear samples + * are converted into 16bit linear ones. + */ +static int dv_extract_audio(uint8_t* frame, uint8_t* ppcm[4], + const DVprofile *sys) +{ + int size, chan, i, j, d, of, smpls, freq, quant, half_ch; + uint16_t lc, rc; + const uint8_t* as_pack; + uint8_t *pcm, ipcm; + + as_pack = dv_extract_pack(frame, dv_audio_source); + if (!as_pack) /* No audio ? */ + return 0; + + smpls = as_pack[1] & 0x3f; /* samples in this frame - min. samples */ + freq = (as_pack[4] >> 3) & 0x07; /* 0 - 48kHz, 1 - 44,1kHz, 2 - 32kHz */ + quant = as_pack[4] & 0x07; /* 0 - 16bit linear, 1 - 12bit nonlinear */ + + if (quant > 1) + return -1; /* unsupported quantization */ + + if (freq >= FF_ARRAY_ELEMS(dv_audio_frequency)) + return AVERROR_INVALIDDATA; + + size = (sys->audio_min_samples[freq] + smpls) * 4; /* 2ch, 2bytes */ + half_ch = sys->difseg_size / 2; + + /* We work with 720p frames split in half, thus even frames have + * channels 0,1 and odd 2,3. */ + ipcm = (sys->height == 720 && !(frame[1] & 0x0C)) ? 2 : 0; + + if (ipcm + sys->n_difchan > (quant == 1 ? 2 : 4)) { + av_log(NULL, AV_LOG_ERROR, "too many dv pcm frames\n"); + return AVERROR_INVALIDDATA; + } + + /* for each DIF channel */ + for (chan = 0; chan < sys->n_difchan; chan++) { + av_assert0(ipcm<4); + pcm = ppcm[ipcm++]; + if (!pcm) + break; + + /* for each DIF segment */ + for (i = 0; i < sys->difseg_size; i++) { + frame += 6 * 80; /* skip DIF segment header */ + if (quant == 1 && i == half_ch) { + /* next stereo channel (12bit mode only) */ + av_assert0(ipcm<4); + pcm = ppcm[ipcm++]; + if (!pcm) + break; + } + + /* for each AV sequence */ + for (j = 0; j < 9; j++) { + for (d = 8; d < 80; d += 2) { + if (quant == 0) { /* 16bit quantization */ + of = sys->audio_shuffle[i][j] + (d - 8) / 2 * sys->audio_stride; + if (of*2 >= size) + continue; + + pcm[of*2] = frame[d+1]; // FIXME: maybe we have to admit + pcm[of*2+1] = frame[d]; // that DV is a big-endian PCM + if (pcm[of*2+1] == 0x80 && pcm[of*2] == 0x00) + pcm[of*2+1] = 0; + } else { /* 12bit quantization */ + lc = ((uint16_t)frame[d] << 4) | + ((uint16_t)frame[d+2] >> 4); + rc = ((uint16_t)frame[d+1] << 4) | + ((uint16_t)frame[d+2] & 0x0f); + lc = (lc == 0x800 ? 0 : dv_audio_12to16(lc)); + rc = (rc == 0x800 ? 0 : dv_audio_12to16(rc)); + + of = sys->audio_shuffle[i%half_ch][j] + (d - 8) / 3 * sys->audio_stride; + if (of*2 >= size) + continue; + + pcm[of*2] = lc & 0xff; // FIXME: maybe we have to admit + pcm[of*2+1] = lc >> 8; // that DV is a big-endian PCM + of = sys->audio_shuffle[i%half_ch+half_ch][j] + + (d - 8) / 3 * sys->audio_stride; + pcm[of*2] = rc & 0xff; // FIXME: maybe we have to admit + pcm[of*2+1] = rc >> 8; // that DV is a big-endian PCM + ++d; + } + } + + frame += 16 * 80; /* 15 Video DIFs + 1 Audio DIF */ + } + } + } + + return size; +} + +static int dv_extract_audio_info(DVDemuxContext* c, uint8_t* frame) +{ + const uint8_t* as_pack; + int freq, stype, smpls, quant, i, ach; + + as_pack = dv_extract_pack(frame, dv_audio_source); + if (!as_pack || !c->sys) { /* No audio ? */ + c->ach = 0; + return 0; + } + + smpls = as_pack[1] & 0x3f; /* samples in this frame - min. samples */ + freq = (as_pack[4] >> 3) & 0x07; /* 0 - 48kHz, 1 - 44,1kHz, 2 - 32kHz */ + stype = (as_pack[3] & 0x1f); /* 0 - 2CH, 2 - 4CH, 3 - 8CH */ + quant = as_pack[4] & 0x07; /* 0 - 16bit linear, 1 - 12bit nonlinear */ + + if (freq >= FF_ARRAY_ELEMS(dv_audio_frequency)) { + av_log(c->fctx, AV_LOG_ERROR, + "Unrecognized audio sample rate index (%d)\n", freq); + return 0; + } + + if (stype > 3) { + av_log(c->fctx, AV_LOG_ERROR, "stype %d is invalid\n", stype); + c->ach = 0; + return 0; + } + + /* note: ach counts PAIRS of channels (i.e. stereo channels) */ + ach = ((int[4]){ 1, 0, 2, 4})[stype]; + if (ach == 1 && quant && freq == 2) + ach = 2; + + /* Dynamic handling of the audio streams in DV */ + for (i = 0; i < ach; i++) { + if (!c->ast[i]) { + c->ast[i] = avformat_new_stream(c->fctx, NULL); + if (!c->ast[i]) + break; + avpriv_set_pts_info(c->ast[i], 64, 1, 30000); + c->ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; + c->ast[i]->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + + av_init_packet(&c->audio_pkt[i]); + c->audio_pkt[i].size = 0; + c->audio_pkt[i].data = c->audio_buf[i]; + c->audio_pkt[i].stream_index = c->ast[i]->index; + c->audio_pkt[i].flags |= AV_PKT_FLAG_KEY; + } + c->ast[i]->codec->sample_rate = dv_audio_frequency[freq]; + c->ast[i]->codec->channels = 2; + c->ast[i]->codec->channel_layout = AV_CH_LAYOUT_STEREO; + c->ast[i]->codec->bit_rate = 2 * dv_audio_frequency[freq] * 16; + c->ast[i]->start_time = 0; + } + c->ach = i; + + return (c->sys->audio_min_samples[freq] + smpls) * 4; /* 2ch, 2bytes */; +} + +static int dv_extract_video_info(DVDemuxContext *c, uint8_t* frame) +{ + const uint8_t* vsc_pack; + AVCodecContext* avctx; + int apt, is16_9; + int size = 0; + + if (c->sys) { + avctx = c->vst->codec; + + avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num, + c->sys->time_base.den); + avctx->time_base= c->sys->time_base; + + /* finding out SAR is a little bit messy */ + vsc_pack = dv_extract_pack(frame, dv_video_control); + apt = frame[4] & 0x07; + is16_9 = (vsc_pack && ((vsc_pack[2] & 0x07) == 0x02 || + (!apt && (vsc_pack[2] & 0x07) == 0x07))); + c->vst->sample_aspect_ratio = c->sys->sar[is16_9]; + avctx->bit_rate = av_rescale_q(c->sys->frame_size, (AVRational){8,1}, + c->sys->time_base); + size = c->sys->frame_size; + } + return size; +} + +static int dv_extract_timecode(DVDemuxContext* c, uint8_t* frame, char *tc) +{ + const uint8_t *tc_pack; + + // For PAL systems, drop frame bit is replaced by an arbitrary + // bit so its value should not be considered. Drop frame timecode + // is only relevant for NTSC systems. + int prevent_df = c->sys->ltc_divisor == 25 || c->sys->ltc_divisor == 50; + + tc_pack = dv_extract_pack(frame, dv_timecode); + if (!tc_pack) + return 0; + av_timecode_make_smpte_tc_string(tc, AV_RB32(tc_pack + 1), prevent_df); + return 1; +} + +/* + * The following 3 functions constitute our interface to the world + */ + +DVDemuxContext* avpriv_dv_init_demux(AVFormatContext *s) +{ + DVDemuxContext *c; + + c = av_mallocz(sizeof(DVDemuxContext)); + if (!c) + return NULL; + + c->vst = avformat_new_stream(s, NULL); + if (!c->vst) { + av_free(c); + return NULL; + } + + c->fctx = s; + c->vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + c->vst->codec->codec_id = AV_CODEC_ID_DVVIDEO; + c->vst->codec->bit_rate = 25000000; + c->vst->start_time = 0; + + return c; +} + +int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt) +{ + int size = -1; + int i; + + for (i = 0; i < c->ach; i++) { + if (c->ast[i] && c->audio_pkt[i].size) { + *pkt = c->audio_pkt[i]; + c->audio_pkt[i].size = 0; + size = pkt->size; + break; + } + } + + return size; +} + +int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, + uint8_t* buf, int buf_size, int64_t pos) +{ + int size, i; + uint8_t *ppcm[4] = {0}; + + if (buf_size < DV_PROFILE_BYTES || + !(c->sys = avpriv_dv_frame_profile(c->sys, buf, buf_size)) || + buf_size < c->sys->frame_size) { + return -1; /* Broken frame, or not enough data */ + } + + /* Queueing audio packet */ + /* FIXME: in case of no audio/bad audio we have to do something */ + size = dv_extract_audio_info(c, buf); + for (i = 0; i < c->ach; i++) { + c->audio_pkt[i].pos = pos; + c->audio_pkt[i].size = size; + c->audio_pkt[i].pts = c->abytes * 30000 * 8 / c->ast[i]->codec->bit_rate; + ppcm[i] = c->audio_buf[i]; + } + if (c->ach) + dv_extract_audio(buf, ppcm, c->sys); + + /* We work with 720p frames split in half, thus even frames have + * channels 0,1 and odd 2,3. */ + if (c->sys->height == 720) { + if (buf[1] & 0x0C) { + c->audio_pkt[2].size = c->audio_pkt[3].size = 0; + } else { + c->audio_pkt[0].size = c->audio_pkt[1].size = 0; + c->abytes += size; + } + } else { + c->abytes += size; + } + + /* Now it's time to return video packet */ + size = dv_extract_video_info(c, buf); + av_init_packet(pkt); + pkt->data = buf; + pkt->pos = pos; + pkt->size = size; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = c->vst->index; + pkt->pts = c->frames; + + c->frames++; + + return size; +} + +static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c, + int64_t timestamp, int flags) +{ + // FIXME: sys may be wrong if last dv_read_packet() failed (buffer is junk) + const DVprofile* sys = avpriv_dv_codec_profile(c->vst->codec); + int64_t offset; + int64_t size = avio_size(s->pb) - s->data_offset; + int64_t max_offset = ((size-1) / sys->frame_size) * sys->frame_size; + + offset = sys->frame_size * timestamp; + + if (size >= 0 && offset > max_offset) offset = max_offset; + else if (offset < 0) offset = 0; + + return offset + s->data_offset; +} + +void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset) +{ + c->frames= frame_offset; + if (c->ach) { + if (c->sys) { + c->abytes= av_rescale_q(c->frames, c->sys->time_base, + (AVRational){8, c->ast[0]->codec->bit_rate}); + }else + av_log(c->fctx, AV_LOG_ERROR, "cannot adjust audio bytes\n"); + } + c->audio_pkt[0].size = c->audio_pkt[1].size = 0; + c->audio_pkt[2].size = c->audio_pkt[3].size = 0; +} + +/************************************************************ + * Implementation of the easiest DV storage of all -- raw DV. + ************************************************************/ + +typedef struct RawDVContext { + DVDemuxContext* dv_demux; + uint8_t buf[DV_MAX_FRAME_SIZE]; +} RawDVContext; + +static int dv_read_timecode(AVFormatContext *s) { + int ret; + char timecode[AV_TIMECODE_STR_SIZE]; + int64_t pos = avio_tell(s->pb); + + // Read 3 DIF blocks: Header block and 2 Subcode blocks. + int partial_frame_size = 3 * 80; + uint8_t *partial_frame = av_mallocz(sizeof(*partial_frame) * + partial_frame_size); + + RawDVContext *c = s->priv_data; + ret = avio_read(s->pb, partial_frame, partial_frame_size); + if (ret < 0) + goto finish; + + if (ret < partial_frame_size) { + ret = -1; + goto finish; + } + + ret = dv_extract_timecode(c->dv_demux, partial_frame, timecode); + if (ret) + av_dict_set(&s->metadata, "timecode", timecode, 0); + else + av_log(s, AV_LOG_ERROR, "Detected timecode is invalid\n"); + +finish: + av_free(partial_frame); + avio_seek(s->pb, pos, SEEK_SET); + return ret; +} + +static int dv_read_header(AVFormatContext *s) +{ + unsigned state, marker_pos = 0; + RawDVContext *c = s->priv_data; + + c->dv_demux = avpriv_dv_init_demux(s); + if (!c->dv_demux) + return -1; + + state = avio_rb32(s->pb); + while ((state & 0xffffff7f) != 0x1f07003f) { + if (url_feof(s->pb)) { + av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n"); + return -1; + } + if (state == 0x003f0700 || state == 0xff3f0700) + marker_pos = avio_tell(s->pb); + if (state == 0xff3f0701 && avio_tell(s->pb) - marker_pos == 80) { + avio_seek(s->pb, -163, SEEK_CUR); + state = avio_rb32(s->pb); + break; + } + state = (state << 8) | avio_r8(s->pb); + } + AV_WB32(c->buf, state); + + if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) != DV_PROFILE_BYTES - 4 || + avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0) + return AVERROR(EIO); + + c->dv_demux->sys = avpriv_dv_frame_profile(c->dv_demux->sys, c->buf, DV_PROFILE_BYTES); + if (!c->dv_demux->sys) { + av_log(s, AV_LOG_ERROR, "Can't determine profile of DV input stream.\n"); + return -1; + } + + s->bit_rate = av_rescale_q(c->dv_demux->sys->frame_size, (AVRational){8,1}, + c->dv_demux->sys->time_base); + + if (s->pb->seekable) + dv_read_timecode(s); + + return 0; +} + + +static int dv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size; + RawDVContext *c = s->priv_data; + + size = avpriv_dv_get_packet(c->dv_demux, pkt); + + if (size < 0) { + int64_t pos = avio_tell(s->pb); + if (!c->dv_demux->sys) + return AVERROR(EIO); + size = c->dv_demux->sys->frame_size; + if (avio_read(s->pb, c->buf, size) <= 0) + return AVERROR(EIO); + + size = avpriv_dv_produce_packet(c->dv_demux, pkt, c->buf, size, pos); + } + + return size; +} + +static int dv_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + RawDVContext *r = s->priv_data; + DVDemuxContext *c = r->dv_demux; + int64_t offset = dv_frame_offset(s, c, timestamp, flags); + + if (avio_seek(s->pb, offset, SEEK_SET) < 0) + return -1; + + ff_dv_offset_reset(c, offset / c->sys->frame_size); + return 0; +} + +static int dv_read_close(AVFormatContext *s) +{ + RawDVContext *c = s->priv_data; + av_free(c->dv_demux); + return 0; +} + +static int dv_probe(AVProbeData *p) +{ + unsigned state, marker_pos = 0; + int i; + int matches = 0; + int secondary_matches = 0; + + if (p->buf_size < 5) + return 0; + + state = AV_RB32(p->buf); + for (i = 4; i < p->buf_size; i++) { + if ((state & 0xffffff7f) == 0x1f07003f) + matches++; + // any section header, also with seq/chan num != 0, + // should appear around every 12000 bytes, at least 10 per frame + if ((state & 0xff07ff7f) == 0x1f07003f) + secondary_matches++; + if (state == 0x003f0700 || state == 0xff3f0700) + marker_pos = i; + if (state == 0xff3f0701 && i - marker_pos == 80) + matches++; + state = (state << 8) | p->buf[i]; + } + + if (matches && p->buf_size / matches < 1024*1024) { + if (matches > 4 || (secondary_matches >= 10 && p->buf_size / secondary_matches < 24000)) + return AVPROBE_SCORE_MAX*3/4; // not max to avoid dv in mov to match + return AVPROBE_SCORE_MAX/4; + } + return 0; +} + +#if CONFIG_DV_DEMUXER +AVInputFormat ff_dv_demuxer = { + .name = "dv", + .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), + .priv_data_size = sizeof(RawDVContext), + .read_probe = dv_probe, + .read_header = dv_read_header, + .read_packet = dv_read_packet, + .read_close = dv_read_close, + .read_seek = dv_read_seek, + .extensions = "dv,dif", +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dv.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dv.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/dv.o libavformat/dv.o: libavformat/dv.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavcodec/dv_profile.h libavcodec/avcodec.h libavcodec/dvdata.h \ + libavcodec/dsputil.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavcodec/rnd_avg.h \ + libavcodec/get_bits.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/dv_profile.h libavutil/channel_layout.h \ + libavutil/intreadwrite.h libavutil/mathematics.h libavutil/timecode.h \ + libavformat/dv.h libavutil/avassert.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dv.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,41 @@ +/* + * General DV muxer/demuxer + * Copyright (c) 2003 Roman Shaposhnik + * + * Many thanks to Dan Dennedy for providing wealth + * of DV technical info. + * + * Raw DV format + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_DV_H +#define AVFORMAT_DV_H + +#include "avformat.h" + +typedef struct DVDemuxContext DVDemuxContext; +DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s); +int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *); +int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t); +void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset); + +typedef struct DVMuxContext DVMuxContext; + +#endif /* AVFORMAT_DV_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dv.o Binary file ffmpeg/libavformat/dv.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dvenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dvenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,419 @@ +/* + * General DV muxer/demuxer + * Copyright (c) 2003 Roman Shaposhnik + * + * Many thanks to Dan Dennedy for providing wealth + * of DV technical info. + * + * Raw DV format + * Copyright (c) 2002 Fabrice Bellard + * + * 50 Mbps (DVCPRO50) support + * Copyright (c) 2006 Daniel Maas + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include "avformat.h" +#include "internal.h" +#include "libavcodec/dv_profile.h" +#include "libavcodec/dvdata.h" +#include "dv.h" +#include "libavutil/fifo.h" +#include "libavutil/mathematics.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" + +#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio + +struct DVMuxContext { + AVClass *av_class; + const DVprofile* sys; /* current DV profile, e.g.: 525/60, 625/50 */ + int n_ast; /* number of stereo audio streams (up to 2) */ + AVStream *ast[2]; /* stereo audio streams */ + AVFifoBuffer *audio_data[2]; /* FIFO for storing excessive amounts of PCM */ + int frames; /* current frame number */ + int64_t start_time; /* recording start time */ + int has_audio; /* frame under construction has audio */ + int has_video; /* frame under construction has video */ + uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under construction */ + AVTimecode tc; /* timecode context */ +}; + +static const int dv_aaux_packs_dist[12][9] = { + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0x50, 0x51, 0x52, 0x53, 0xff, 0xff }, + { 0x50, 0x51, 0x52, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff }, +}; + +static int dv_audio_frame_size(const DVprofile* sys, int frame) +{ + return sys->audio_samples_dist[frame % (sizeof(sys->audio_samples_dist) / + sizeof(sys->audio_samples_dist[0]))]; +} + +static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* buf, ...) +{ + struct tm tc; + time_t ct; + uint32_t timecode; + va_list ap; + + buf[0] = (uint8_t)pack_id; + switch (pack_id) { + case dv_timecode: + timecode = av_timecode_get_smpte_from_framenum(&c->tc, c->frames); + timecode |= 1<<23 | 1<<15 | 1<<7 | 1<<6; // biphase and binary group flags + AV_WB32(buf + 1, timecode); + break; + case dv_audio_source: /* AAUX source pack */ + va_start(ap, buf); + buf[1] = (1 << 7) | /* locked mode -- SMPTE only supports locked mode */ + (1 << 6) | /* reserved -- always 1 */ + (dv_audio_frame_size(c->sys, c->frames) - + c->sys->audio_min_samples[0]); + /* # of samples */ + buf[2] = (0 << 7) | /* multi-stereo */ + (0 << 5) | /* #of audio channels per block: 0 -- 1 channel */ + (0 << 4) | /* pair bit: 0 -- one pair of channels */ + !!va_arg(ap, int); /* audio mode */ + buf[3] = (1 << 7) | /* res */ + (1 << 6) | /* multi-language flag */ + (c->sys->dsf << 5) | /* system: 60fields/50fields */ + (c->sys->n_difchan & 2); /* definition: 0 -- 25Mbps, 2 -- 50Mbps */ + buf[4] = (1 << 7) | /* emphasis: 1 -- off */ + (0 << 6) | /* emphasis time constant: 0 -- reserved */ + (0 << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */ + 0; /* quantization: 0 -- 16bit linear, 1 -- 12bit nonlinear */ + va_end(ap); + break; + case dv_audio_control: + buf[1] = (0 << 6) | /* copy protection: 0 -- unrestricted */ + (1 << 4) | /* input source: 1 -- digital input */ + (3 << 2) | /* compression: 3 -- no information */ + 0; /* misc. info/SMPTE emphasis off */ + buf[2] = (1 << 7) | /* recording start point: 1 -- no */ + (1 << 6) | /* recording end point: 1 -- no */ + (1 << 3) | /* recording mode: 1 -- original */ + 7; + buf[3] = (1 << 7) | /* direction: 1 -- forward */ + (c->sys->pix_fmt == AV_PIX_FMT_YUV420P ? 0x20 : /* speed */ + c->sys->ltc_divisor * 4); + buf[4] = (1 << 7) | /* reserved -- always 1 */ + 0x7f; /* genre category */ + break; + case dv_audio_recdate: + case dv_video_recdate: /* VAUX recording date */ + ct = c->start_time + av_rescale_rnd(c->frames, c->sys->time_base.num, + c->sys->time_base.den, AV_ROUND_DOWN); + ff_brktimegm(ct, &tc); + buf[1] = 0xff; /* ds, tm, tens of time zone, units of time zone */ + /* 0xff is very likely to be "unknown" */ + buf[2] = (3 << 6) | /* reserved -- always 1 */ + ((tc.tm_mday / 10) << 4) | /* Tens of day */ + (tc.tm_mday % 10); /* Units of day */ + buf[3] = /* we set high 4 bits to 0, shouldn't we set them to week? */ + ((tc.tm_mon / 10) << 4) | /* Tens of month */ + (tc.tm_mon % 10); /* Units of month */ + buf[4] = (((tc.tm_year % 100) / 10) << 4) | /* Tens of year */ + (tc.tm_year % 10); /* Units of year */ + break; + case dv_audio_rectime: /* AAUX recording time */ + case dv_video_rectime: /* VAUX recording time */ + ct = c->start_time + av_rescale_rnd(c->frames, c->sys->time_base.num, + c->sys->time_base.den, AV_ROUND_DOWN); + ff_brktimegm(ct, &tc); + buf[1] = (3 << 6) | /* reserved -- always 1 */ + 0x3f; /* tens of frame, units of frame: 0x3f - "unknown" ? */ + buf[2] = (1 << 7) | /* reserved -- always 1 */ + ((tc.tm_sec / 10) << 4) | /* Tens of seconds */ + (tc.tm_sec % 10); /* Units of seconds */ + buf[3] = (1 << 7) | /* reserved -- always 1 */ + ((tc.tm_min / 10) << 4) | /* Tens of minutes */ + (tc.tm_min % 10); /* Units of minutes */ + buf[4] = (3 << 6) | /* reserved -- always 1 */ + ((tc.tm_hour / 10) << 4) | /* Tens of hours */ + (tc.tm_hour % 10); /* Units of hours */ + break; + default: + buf[1] = buf[2] = buf[3] = buf[4] = 0xff; + } + return 5; +} + +static void dv_inject_audio(DVMuxContext *c, int channel, uint8_t* frame_ptr) +{ + int i, j, d, of, size; + size = 4 * dv_audio_frame_size(c->sys, c->frames); + frame_ptr += channel * c->sys->difseg_size * 150 * 80; + for (i = 0; i < c->sys->difseg_size; i++) { + frame_ptr += 6 * 80; /* skip DIF segment header */ + for (j = 0; j < 9; j++) { + dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], i >= c->sys->difseg_size/2); + for (d = 8; d < 80; d+=2) { + of = c->sys->audio_shuffle[i][j] + (d - 8)/2 * c->sys->audio_stride; + if (of*2 >= size) + continue; + + frame_ptr[d] = *av_fifo_peek2(c->audio_data[channel], of*2+1); // FIXME: maybe we have to admit + frame_ptr[d+1] = *av_fifo_peek2(c->audio_data[channel], of*2); // that DV is a big-endian PCM + } + frame_ptr += 16 * 80; /* 15 Video DIFs + 1 Audio DIF */ + } + } +} + +static void dv_inject_metadata(DVMuxContext *c, uint8_t* frame) +{ + int j, k; + uint8_t* buf; + + for (buf = frame; buf < frame + c->sys->frame_size; buf += 150 * 80) { + /* DV subcode: 2nd and 3d DIFs */ + for (j = 80; j < 80 * 3; j += 80) { + for (k = 6; k < 6 * 8; k += 8) + dv_write_pack(dv_timecode, c, &buf[j+k]); + + if (((long)(buf-frame)/(c->sys->frame_size/(c->sys->difseg_size*c->sys->n_difchan))%c->sys->difseg_size) > 5) { /* FIXME: is this really needed ? */ + dv_write_pack(dv_video_recdate, c, &buf[j+14]); + dv_write_pack(dv_video_rectime, c, &buf[j+22]); + dv_write_pack(dv_video_recdate, c, &buf[j+38]); + dv_write_pack(dv_video_rectime, c, &buf[j+46]); + } + } + + /* DV VAUX: 4th, 5th and 6th 3DIFs */ + for (j = 80*3 + 3; j < 80*6; j += 80) { + dv_write_pack(dv_video_recdate, c, &buf[j+5*2]); + dv_write_pack(dv_video_rectime, c, &buf[j+5*3]); + dv_write_pack(dv_video_recdate, c, &buf[j+5*11]); + dv_write_pack(dv_video_rectime, c, &buf[j+5*12]); + } + } +} + +/* + * The following 3 functions constitute our interface to the world + */ + +static int dv_assemble_frame(DVMuxContext *c, AVStream* st, + uint8_t* data, int data_size, uint8_t** frame) +{ + int i, reqasize; + + *frame = &c->frame_buf[0]; + reqasize = 4 * dv_audio_frame_size(c->sys, c->frames); + + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + /* FIXME: we have to have more sensible approach than this one */ + if (c->has_video) + av_log(st->codec, AV_LOG_ERROR, "Can't process DV frame #%d. Insufficient audio data or severe sync problem.\n", c->frames); + + memcpy(*frame, data, c->sys->frame_size); + c->has_video = 1; + break; + case AVMEDIA_TYPE_AUDIO: + for (i = 0; i < c->n_ast && st != c->ast[i]; i++); + + /* FIXME: we have to have more sensible approach than this one */ + if (av_fifo_size(c->audio_data[i]) + data_size >= 100*MAX_AUDIO_FRAME_SIZE) + av_log(st->codec, AV_LOG_ERROR, "Can't process DV frame #%d. Insufficient video data or severe sync problem.\n", c->frames); + av_fifo_generic_write(c->audio_data[i], data, data_size, NULL); + + /* Let us see if we've got enough audio for one DV frame. */ + c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i); + + break; + default: + break; + } + + /* Let us see if we have enough data to construct one DV frame. */ + if (c->has_video == 1 && c->has_audio + 1 == 1 << c->n_ast) { + dv_inject_metadata(c, *frame); + c->has_audio = 0; + for (i=0; i < c->n_ast; i++) { + dv_inject_audio(c, i, *frame); + av_fifo_drain(c->audio_data[i], reqasize); + c->has_audio |= ((reqasize <= av_fifo_size(c->audio_data[i])) << i); + } + + c->has_video = 0; + + c->frames++; + + return c->sys->frame_size; + } + + return 0; +} + +static DVMuxContext* dv_init_mux(AVFormatContext* s) +{ + DVMuxContext *c = s->priv_data; + AVStream *vst = NULL; + AVDictionaryEntry *t; + int i; + + /* we support at most 1 video and 2 audio streams */ + if (s->nb_streams > 3) + return NULL; + + c->n_ast = 0; + c->ast[0] = c->ast[1] = NULL; + + /* We have to sort out where audio and where video stream is */ + for (i=0; inb_streams; i++) { + switch (s->streams[i]->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (vst) return NULL; + vst = s->streams[i]; + break; + case AVMEDIA_TYPE_AUDIO: + if (c->n_ast > 1) return NULL; + c->ast[c->n_ast++] = s->streams[i]; + break; + default: + goto bail_out; + } + } + + /* Some checks -- DV format is very picky about its incoming streams */ + if (!vst || vst->codec->codec_id != AV_CODEC_ID_DVVIDEO) + goto bail_out; + for (i=0; in_ast; i++) { + if (c->ast[i] && (c->ast[i]->codec->codec_id != AV_CODEC_ID_PCM_S16LE || + c->ast[i]->codec->sample_rate != 48000 || + c->ast[i]->codec->channels != 2)) + goto bail_out; + } + c->sys = avpriv_dv_codec_profile(vst->codec); + if (!c->sys) + goto bail_out; + + if ((c->n_ast > 1) && (c->sys->n_difchan < 2)) { + /* only 1 stereo pair is allowed in 25Mbps mode */ + goto bail_out; + } + + /* Ok, everything seems to be in working order */ + c->frames = 0; + c->has_audio = 0; + c->has_video = 0; + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) + c->start_time = ff_iso8601_to_unix_time(t->value); + + for (i=0; i < c->n_ast; i++) { + if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc(100*MAX_AUDIO_FRAME_SIZE))) { + while (i > 0) { + i--; + av_fifo_free(c->audio_data[i]); + } + goto bail_out; + } + } + + return c; + +bail_out: + return NULL; +} + +static void dv_delete_mux(DVMuxContext *c) +{ + int i; + for (i=0; i < c->n_ast; i++) + av_fifo_free(c->audio_data[i]); +} + +static int dv_write_header(AVFormatContext *s) +{ + AVRational rate; + DVMuxContext *dvc = s->priv_data; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + + if (!dv_init_mux(s)) { + av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n" + "Make sure that you supply exactly two streams:\n" + " video: 25fps or 29.97fps, audio: 2ch/48kHz/PCM\n" + " (50Mbps allows an optional second audio stream)\n"); + return -1; + } + rate.num = dvc->sys->ltc_divisor; + rate.den = 1; + if (!tcr) { // no global timecode, look into the streams + int i; + for (i = 0; i < s->nb_streams; i++) { + tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0); + if (tcr) + break; + } + } + if (tcr && av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s) >= 0) + return 0; + return av_timecode_init(&dvc->tc, rate, 0, 0, s); +} + +static int dv_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + uint8_t* frame; + int fsize; + + fsize = dv_assemble_frame(s->priv_data, s->streams[pkt->stream_index], + pkt->data, pkt->size, &frame); + if (fsize > 0) { + avio_write(s->pb, frame, fsize); + } + return 0; +} + +/* + * We might end up with some extra A/V data without matching counterpart. + * E.g. video data without enough audio to write the complete frame. + * Currently we simply drop the last frame. I don't know whether this + * is the best strategy of all + */ +static int dv_write_trailer(struct AVFormatContext *s) +{ + dv_delete_mux(s->priv_data); + return 0; +} + +AVOutputFormat ff_dv_muxer = { + .name = "dv", + .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), + .extensions = "dv", + .priv_data_size = sizeof(DVMuxContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_DVVIDEO, + .write_header = dv_write_header, + .write_packet = dv_write_packet, + .write_trailer = dv_write_trailer, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dvenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dvenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/dvenc.o libavformat/dvenc.o: libavformat/dvenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavcodec/dv_profile.h libavcodec/avcodec.h libavcodec/dvdata.h \ + libavcodec/dsputil.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavcodec/rnd_avg.h \ + libavcodec/get_bits.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/dv_profile.h libavformat/dv.h libavutil/fifo.h \ + libavutil/mathematics.h libavutil/intreadwrite.h libavutil/opt.h \ + libavutil/timecode.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dvenc.o Binary file ffmpeg/libavformat/dvenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dxa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dxa.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,225 @@ +/* + * DXA demuxer + * Copyright (c) 2007 Konstantin Shishkov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "riff.h" + +#define DXA_EXTRA_SIZE 9 + +typedef struct{ + int frames; + int has_sound; + int bpc; + uint32_t bytes_left; + int64_t wavpos, vidpos; + int readvid; +}DXAContext; + +static int dxa_probe(AVProbeData *p) +{ + int w, h; + if (p->buf_size < 15) + return 0; + w = AV_RB16(p->buf + 11); + h = AV_RB16(p->buf + 13); + /* check file header */ + if (p->buf[0] == 'D' && p->buf[1] == 'E' && + p->buf[2] == 'X' && p->buf[3] == 'A' && + w && w <= 2048 && h && h <= 2048) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +static int dxa_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + DXAContext *c = s->priv_data; + AVStream *st, *ast; + uint32_t tag; + int32_t fps; + int w, h; + int num, den; + int flags; + int ret; + + tag = avio_rl32(pb); + if (tag != MKTAG('D', 'E', 'X', 'A')) + return AVERROR_INVALIDDATA; + flags = avio_r8(pb); + c->frames = avio_rb16(pb); + if(!c->frames){ + av_log(s, AV_LOG_ERROR, "File contains no frames ???\n"); + return AVERROR_INVALIDDATA; + } + + fps = avio_rb32(pb); + if(fps > 0){ + den = 1000; + num = fps; + }else if (fps < 0){ + den = 100000; + num = -fps; + }else{ + den = 10; + num = 1; + } + w = avio_rb16(pb); + h = avio_rb16(pb); + c->has_sound = 0; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + // Parse WAV data header + if(avio_rl32(pb) == MKTAG('W', 'A', 'V', 'E')){ + uint32_t size, fsize; + c->has_sound = 1; + size = avio_rb32(pb); + c->vidpos = avio_tell(pb) + size; + avio_skip(pb, 16); + fsize = avio_rl32(pb); + + ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ret = ff_get_wav_header(pb, ast->codec, fsize); + if (ret < 0) + return ret; + if (ast->codec->sample_rate > 0) + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + // find 'data' chunk + while(avio_tell(pb) < c->vidpos && !url_feof(pb)){ + tag = avio_rl32(pb); + fsize = avio_rl32(pb); + if(tag == MKTAG('d', 'a', 't', 'a')) break; + avio_skip(pb, fsize); + } + c->bpc = (fsize + c->frames - 1) / c->frames; + if(ast->codec->block_align) + c->bpc = ((c->bpc + ast->codec->block_align - 1) / ast->codec->block_align) * ast->codec->block_align; + c->bytes_left = fsize; + c->wavpos = avio_tell(pb); + avio_seek(pb, c->vidpos, SEEK_SET); + } + + /* now we are ready: build format streams */ + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DXA; + st->codec->width = w; + st->codec->height = h; + av_reduce(&den, &num, den, num, (1UL<<31)-1); + avpriv_set_pts_info(st, 33, num, den); + /* flags & 0x80 means that image is interlaced, + * flags & 0x40 means that image has double height + * either way set true height + */ + if(flags & 0xC0){ + st->codec->height >>= 1; + } + c->readvid = !c->has_sound; + c->vidpos = avio_tell(pb); + s->start_time = 0; + s->duration = (int64_t)c->frames * AV_TIME_BASE * num / den; + av_log(s, AV_LOG_DEBUG, "%d frame(s)\n",c->frames); + + return 0; +} + +static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DXAContext *c = s->priv_data; + int ret; + uint32_t size; + uint8_t buf[DXA_EXTRA_SIZE], pal[768+4]; + int pal_size = 0; + + if(!c->readvid && c->has_sound && c->bytes_left){ + c->readvid = 1; + avio_seek(s->pb, c->wavpos, SEEK_SET); + size = FFMIN(c->bytes_left, c->bpc); + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 1; + if(ret != size) + return AVERROR(EIO); + c->bytes_left -= size; + c->wavpos = avio_tell(s->pb); + return 0; + } + avio_seek(s->pb, c->vidpos, SEEK_SET); + while(!url_feof(s->pb) && c->frames){ + avio_read(s->pb, buf, 4); + switch(AV_RL32(buf)){ + case MKTAG('N', 'U', 'L', 'L'): + if(av_new_packet(pkt, 4 + pal_size) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 0; + if(pal_size) memcpy(pkt->data, pal, pal_size); + memcpy(pkt->data + pal_size, buf, 4); + c->frames--; + c->vidpos = avio_tell(s->pb); + c->readvid = 0; + return 0; + case MKTAG('C', 'M', 'A', 'P'): + pal_size = 768+4; + memcpy(pal, buf, 4); + avio_read(s->pb, pal + 4, 768); + break; + case MKTAG('F', 'R', 'A', 'M'): + avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4); + size = AV_RB32(buf + 5); + if(size > 0xFFFFFF){ + av_log(s, AV_LOG_ERROR, "Frame size is too big: %d\n", size); + return AVERROR_INVALIDDATA; + } + if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data + pal_size, buf, DXA_EXTRA_SIZE); + ret = avio_read(s->pb, pkt->data + DXA_EXTRA_SIZE + pal_size, size); + if(ret != size){ + av_free_packet(pkt); + return AVERROR(EIO); + } + if(pal_size) memcpy(pkt->data, pal, pal_size); + pkt->stream_index = 0; + c->frames--; + c->vidpos = avio_tell(s->pb); + c->readvid = 0; + return 0; + default: + av_log(s, AV_LOG_ERROR, "Unknown tag %c%c%c%c\n", buf[0], buf[1], buf[2], buf[3]); + return AVERROR_INVALIDDATA; + } + } + return AVERROR_EOF; +} + +AVInputFormat ff_dxa_demuxer = { + .name = "dxa", + .long_name = NULL_IF_CONFIG_SMALL("DXA"), + .priv_data_size = sizeof(DXAContext), + .read_probe = dxa_probe, + .read_header = dxa_read_header, + .read_packet = dxa_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dxa.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/dxa.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/dxa.o libavformat/dxa.o: libavformat/dxa.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/riff.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/dxa.o Binary file ffmpeg/libavformat/dxa.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/eacdata.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/eacdata.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,105 @@ +/* + * Electronic Arts .cdata file Demuxer + * Copyright (c) 2007 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Electronic Arts cdata Format Demuxer + * by Peter Ross (pross@xvid.org) + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=EA_Command_And_Conquer_3_Audio_Codec + */ + +#include "avformat.h" +#include "internal.h" + +typedef struct { + unsigned int channels; + unsigned int audio_pts; +} CdataDemuxContext; + +static int cdata_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (b[0] == 0x04 && (b[1] == 0x00 || b[1] == 0x04 || b[1] == 0x0C || b[1] == 0x14)) + return AVPROBE_SCORE_MAX/8; + return 0; +} + +static int cdata_read_header(AVFormatContext *s) +{ + CdataDemuxContext *cdata = s->priv_data; + AVIOContext *pb = s->pb; + unsigned int sample_rate, header; + AVStream *st; + int64_t channel_layout = 0; + + header = avio_rb16(pb); + switch (header) { + case 0x0400: cdata->channels = 1; break; + case 0x0404: cdata->channels = 2; break; + case 0x040C: cdata->channels = 4; channel_layout = AV_CH_LAYOUT_QUAD; break; + case 0x0414: cdata->channels = 6; channel_layout = AV_CH_LAYOUT_5POINT1_BACK; break; + default: + av_log(s, AV_LOG_INFO, "unknown header 0x%04x\n", header); + return -1; + }; + + sample_rate = avio_rb16(pb); + avio_skip(pb, (avio_r8(pb) & 0x20) ? 15 : 11); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->codec_id = AV_CODEC_ID_ADPCM_EA_XAS; + st->codec->channels = cdata->channels; + st->codec->channel_layout = channel_layout; + st->codec->sample_rate = sample_rate; + avpriv_set_pts_info(st, 64, 1, sample_rate); + + cdata->audio_pts = 0; + return 0; +} + +static int cdata_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + CdataDemuxContext *cdata = s->priv_data; + int packet_size = 76*cdata->channels; + + int ret = av_get_packet(s->pb, pkt, packet_size); + if (ret < 0) + return ret; + pkt->pts = cdata->audio_pts++; + return 0; +} + +AVInputFormat ff_ea_cdata_demuxer = { + .name = "ea_cdata", + .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts cdata"), + .priv_data_size = sizeof(CdataDemuxContext), + .read_probe = cdata_probe, + .read_header = cdata_read_header, + .read_packet = cdata_read_packet, + .extensions = "cdata", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/eacdata.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/eacdata.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/eacdata.o libavformat/eacdata.o: libavformat/eacdata.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/eacdata.o Binary file ffmpeg/libavformat/eacdata.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/electronicarts.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/electronicarts.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,640 @@ +/* Electronic Arts Multimedia File Demuxer + * Copyright (c) 2004 The ffmpeg Project + * Copyright (c) 2006-2008 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Electronic Arts Multimedia file demuxer (WVE/UV2/etc.) + * by Robin Kay (komadori at gekkou.co.uk) + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define SCHl_TAG MKTAG('S', 'C', 'H', 'l') +#define SEAD_TAG MKTAG('S', 'E', 'A', 'D') /* Sxxx header */ +#define SNDC_TAG MKTAG('S', 'N', 'D', 'C') /* Sxxx data */ +#define SEND_TAG MKTAG('S', 'E', 'N', 'D') /* Sxxx end */ +#define SHEN_TAG MKTAG('S', 'H', 'E', 'N') /* SxEN header */ +#define SDEN_TAG MKTAG('S', 'D', 'E', 'N') /* SxEN data */ +#define SEEN_TAG MKTAG('S', 'E', 'E', 'N') /* SxEN end */ +#define ISNh_TAG MKTAG('1', 'S', 'N', 'h') /* 1SNx header */ +#define EACS_TAG MKTAG('E', 'A', 'C', 'S') +#define ISNd_TAG MKTAG('1', 'S', 'N', 'd') /* 1SNx data */ +#define ISNe_TAG MKTAG('1', 'S', 'N', 'e') /* 1SNx end */ +#define PT00_TAG MKTAG('P', 'T', 0x0, 0x0) +#define GSTR_TAG MKTAG('G', 'S', 'T', 'R') +#define SCDl_TAG MKTAG('S', 'C', 'D', 'l') +#define SCEl_TAG MKTAG('S', 'C', 'E', 'l') +#define kVGT_TAG MKTAG('k', 'V', 'G', 'T') /* TGV i-frame */ +#define fVGT_TAG MKTAG('f', 'V', 'G', 'T') /* TGV p-frame */ +#define mTCD_TAG MKTAG('m', 'T', 'C', 'D') /* MDEC */ +#define MADk_TAG MKTAG('M', 'A', 'D', 'k') /* MAD i-frame */ +#define MADm_TAG MKTAG('M', 'A', 'D', 'm') /* MAD p-frame */ +#define MADe_TAG MKTAG('M', 'A', 'D', 'e') /* MAD lqp-frame */ +#define MPCh_TAG MKTAG('M', 'P', 'C', 'h') /* MPEG2 */ +#define TGQs_TAG MKTAG('T', 'G', 'Q', 's') /* TGQ i-frame (appears in .TGQ files) */ +#define pQGT_TAG MKTAG('p', 'Q', 'G', 'T') /* TGQ i-frame (appears in .UV files) */ +#define pIQT_TAG MKTAG('p', 'I', 'Q', 'T') /* TQI/UV2 i-frame (.UV2/.WVE) */ +#define MVhd_TAG MKTAG('M', 'V', 'h', 'd') +#define MV0K_TAG MKTAG('M', 'V', '0', 'K') +#define MV0F_TAG MKTAG('M', 'V', '0', 'F') +#define MVIh_TAG MKTAG('M', 'V', 'I', 'h') /* CMV header */ +#define MVIf_TAG MKTAG('M', 'V', 'I', 'f') /* CMV i-frame */ + +typedef struct EaDemuxContext { + int big_endian; + + enum AVCodecID video_codec; + AVRational time_base; + int width, height; + int nb_frames; + int video_stream_index; + + enum AVCodecID audio_codec; + int audio_stream_index; + + int bytes; + int sample_rate; + int num_channels; + int num_samples; +} EaDemuxContext; + +static uint32_t read_arbitary(AVIOContext *pb) { + uint8_t size, byte; + int i; + uint32_t word; + + size = avio_r8(pb); + + word = 0; + for (i = 0; i < size; i++) { + byte = avio_r8(pb); + word <<= 8; + word |= byte; + } + + return word; +} + +/* + * Process PT/GSTR sound header + * return 1 if success, 0 if invalid format, otherwise AVERROR_xxx + */ +static int process_audio_header_elements(AVFormatContext *s) +{ + int inHeader = 1; + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + int compression_type = -1, revision = -1, revision2 = -1; + + ea->bytes = 2; + ea->sample_rate = -1; + ea->num_channels = 1; + + while (!url_feof(pb) && inHeader) { + int inSubheader; + uint8_t byte; + byte = avio_r8(pb); + + switch (byte) { + case 0xFD: + av_log (s, AV_LOG_DEBUG, "entered audio subheader\n"); + inSubheader = 1; + while (!url_feof(pb) && inSubheader) { + uint8_t subbyte; + subbyte = avio_r8(pb); + + switch (subbyte) { + case 0x80: + revision = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "revision (element 0x80) set to 0x%08x\n", revision); + break; + case 0x82: + ea->num_channels = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "num_channels (element 0x82) set to 0x%08x\n", ea->num_channels); + break; + case 0x83: + compression_type = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "compression_type (element 0x83) set to 0x%08x\n", compression_type); + break; + case 0x84: + ea->sample_rate = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "sample_rate (element 0x84) set to %i\n", ea->sample_rate); + break; + case 0x85: + ea->num_samples = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "num_samples (element 0x85) set to 0x%08x\n", ea->num_samples); + break; + case 0x8A: + av_log (s, AV_LOG_DEBUG, "element 0x%02x set to 0x%08x\n", subbyte, read_arbitary(pb)); + av_log (s, AV_LOG_DEBUG, "exited audio subheader\n"); + inSubheader = 0; + break; + case 0xA0: + revision2 = read_arbitary(pb); + av_log (s, AV_LOG_DEBUG, "revision2 (element 0xA0) set to 0x%08x\n", revision2); + break; + case 0xFF: + av_log (s, AV_LOG_DEBUG, "end of header block reached (within audio subheader)\n"); + inSubheader = 0; + inHeader = 0; + break; + default: + av_log (s, AV_LOG_DEBUG, "element 0x%02x set to 0x%08x\n", subbyte, read_arbitary(pb)); + break; + } + } + break; + case 0xFF: + av_log (s, AV_LOG_DEBUG, "end of header block reached\n"); + inHeader = 0; + break; + default: + av_log (s, AV_LOG_DEBUG, "header element 0x%02x set to 0x%08x\n", byte, read_arbitary(pb)); + break; + } + } + + switch (compression_type) { + case 0: ea->audio_codec = AV_CODEC_ID_PCM_S16LE; break; + case 7: ea->audio_codec = AV_CODEC_ID_ADPCM_EA; break; + case -1: + switch (revision) { + case 1: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; break; + case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; break; + case 3: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R3; break; + case -1: break; + default: + avpriv_request_sample(s, "stream type; revision=%i", revision); + return 0; + } + switch (revision2) { + case 8: ea->audio_codec = AV_CODEC_ID_PCM_S16LE_PLANAR; break; + case 10: + switch (revision) { + case -1: + case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R1; break; + case 3: ea->audio_codec = AV_CODEC_ID_ADPCM_EA_R2; break; + default: + avpriv_request_sample(s, "stream type; revision=%i, revision2=%i", revision, revision2); + return 0; + } + break; + case 16: ea->audio_codec = AV_CODEC_ID_MP3; break; + case -1: break; + default: + ea->audio_codec = AV_CODEC_ID_NONE; + avpriv_request_sample(s, "stream type; revision2=%i", revision2); + return 0; + } + break; + default: + avpriv_request_sample(s, "stream type; compression_type=%i", compression_type); + return 0; + } + + if (ea->sample_rate == -1) + ea->sample_rate = revision==3 ? 48000 : 22050; + + return 1; +} + +/* + * Process EACS sound header + * return 1 if success, 0 if invalid format, otherwise AVERROR_xxx + */ +static int process_audio_header_eacs(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + int compression_type; + + ea->sample_rate = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); + ea->bytes = avio_r8(pb); /* 1=8-bit, 2=16-bit */ + ea->num_channels = avio_r8(pb); + compression_type = avio_r8(pb); + avio_skip(pb, 13); + + switch (compression_type) { + case 0: + switch (ea->bytes) { + case 1: ea->audio_codec = AV_CODEC_ID_PCM_S8; break; + case 2: ea->audio_codec = AV_CODEC_ID_PCM_S16LE; break; + } + break; + case 1: ea->audio_codec = AV_CODEC_ID_PCM_MULAW; ea->bytes = 1; break; + case 2: ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_EACS; break; + default: + avpriv_request_sample(s, "stream type; audio compression_type=%i", compression_type); + } + + return 1; +} + +/* + * Process SEAD sound header + * return 1 if success, 0 if invalid format, otherwise AVERROR_xxx + */ +static int process_audio_header_sead(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + + ea->sample_rate = avio_rl32(pb); + ea->bytes = avio_rl32(pb); /* 1=8-bit, 2=16-bit */ + ea->num_channels = avio_rl32(pb); + ea->audio_codec = AV_CODEC_ID_ADPCM_IMA_EA_SEAD; + + return 1; +} + +static int process_video_header_mdec(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + avio_skip(pb, 4); + ea->width = avio_rl16(pb); + ea->height = avio_rl16(pb); + ea->time_base = (AVRational){1,15}; + ea->video_codec = AV_CODEC_ID_MDEC; + return 1; +} + +static int process_video_header_vp6(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + + avio_skip(pb, 8); + ea->nb_frames = avio_rl32(pb); + avio_skip(pb, 4); + ea->time_base.den = avio_rl32(pb); + ea->time_base.num = avio_rl32(pb); + if (ea->time_base.den <= 0 || ea->time_base.num <= 0) { + av_log(s, AV_LOG_ERROR, "Timebase is invalid\n"); + return AVERROR_INVALIDDATA; + } + ea->video_codec = AV_CODEC_ID_VP6; + + return 1; +} + +static int process_video_header_cmv(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + int fps; + + avio_skip(s->pb, 10); + fps = avio_rl16(s->pb); + if (fps) + ea->time_base = (AVRational){1, fps}; + ea->video_codec = AV_CODEC_ID_CMV; + + return 0; +} + +/* + * Process EA file header + * Returns 1 if the EA file is valid and successfully opened, 0 otherwise + */ +static int process_ea_header(AVFormatContext *s) { + uint32_t blockid, size = 0; + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + int i; + + for (i=0; i<5 && (!ea->audio_codec || !ea->video_codec); i++) { + unsigned int startpos = avio_tell(pb); + int err = 0; + + blockid = avio_rl32(pb); + size = avio_rl32(pb); + if (i == 0) + ea->big_endian = size > 0x000FFFFF; + if (ea->big_endian) + size = av_bswap32(size); + + switch (blockid) { + case ISNh_TAG: + if (avio_rl32(pb) != EACS_TAG) { + avpriv_request_sample(s, "unknown 1SNh headerid"); + return 0; + } + err = process_audio_header_eacs(s); + break; + + case SCHl_TAG : + case SHEN_TAG : + blockid = avio_rl32(pb); + if (blockid == GSTR_TAG) { + avio_skip(pb, 4); + } else if ((blockid & 0xFFFF)!=PT00_TAG) { + avpriv_request_sample(s, "unknown SCHl headerid"); + return 0; + } + err = process_audio_header_elements(s); + break; + + case SEAD_TAG: + err = process_audio_header_sead(s); + break; + + case MVIh_TAG : + err = process_video_header_cmv(s); + break; + + case kVGT_TAG: + ea->video_codec = AV_CODEC_ID_TGV; + break; + + case mTCD_TAG : + err = process_video_header_mdec(s); + break; + + case MPCh_TAG: + ea->video_codec = AV_CODEC_ID_MPEG2VIDEO; + break; + + case pQGT_TAG: + case TGQs_TAG: + ea->video_codec = AV_CODEC_ID_TGQ; + break; + + case pIQT_TAG: + ea->video_codec = AV_CODEC_ID_TQI; + break; + + case MADk_TAG : + ea->video_codec = AV_CODEC_ID_MAD; + break; + + case MVhd_TAG : + err = process_video_header_vp6(s); + break; + } + + if (err < 0) { + av_log(s, AV_LOG_ERROR, "error parsing header: %i\n", err); + return err; + } + + avio_seek(pb, startpos + size, SEEK_SET); + } + + avio_seek(pb, 0, SEEK_SET); + + return 1; +} + + +static int ea_probe(AVProbeData *p) +{ + switch (AV_RL32(&p->buf[0])) { + case ISNh_TAG: + case SCHl_TAG: + case SEAD_TAG: + case SHEN_TAG: + case kVGT_TAG: + case MADk_TAG: + case MPCh_TAG: + case MVhd_TAG: + case MVIh_TAG: + break; + default: + return 0; + } + if (AV_RL32(&p->buf[4]) > 0xfffff && AV_RB32(&p->buf[4]) > 0xfffff) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int ea_read_header(AVFormatContext *s) +{ + EaDemuxContext *ea = s->priv_data; + AVStream *st; + + if (process_ea_header(s)<=0) + return AVERROR(EIO); + + if (ea->video_codec) { + /* initialize the video decoder stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + ea->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = ea->video_codec; + // parsing is necessary to make FFmpeg generate correct timestamps + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) + st->need_parsing = AVSTREAM_PARSE_HEADERS; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = ea->width; + st->codec->height = ea->height; + st->duration = st->nb_frames = ea->nb_frames; + if (ea->time_base.num) + avpriv_set_pts_info(st, 64, ea->time_base.num, ea->time_base.den); + st->r_frame_rate = + st->avg_frame_rate = av_inv_q(ea->time_base); + } + + if (ea->audio_codec) { + if (ea->num_channels <= 0) { + av_log(s, AV_LOG_WARNING, "Unsupported number of channels: %d\n", ea->num_channels); + ea->audio_codec = 0; + return 1; + } + if (ea->sample_rate <= 0) { + av_log(s, AV_LOG_ERROR, "Unsupported sample rate: %d\n", ea->sample_rate); + ea->audio_codec = 0; + return 1; + } + if (ea->bytes <= 0) { + av_log(s, AV_LOG_ERROR, "Invalid number of bytes per sample: %d\n", ea->bytes); + ea->audio_codec = AV_CODEC_ID_NONE; + return 1; + } + + /* initialize the audio decoder stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 33, 1, ea->sample_rate); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = ea->audio_codec; + st->codec->codec_tag = 0; /* no tag */ + st->codec->channels = ea->num_channels; + st->codec->sample_rate = ea->sample_rate; + st->codec->bits_per_coded_sample = ea->bytes * 8; + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * + st->codec->bits_per_coded_sample / 4; + st->codec->block_align = st->codec->channels*st->codec->bits_per_coded_sample; + ea->audio_stream_index = st->index; + st->start_time = 0; + } + + return 1; +} + +static int ea_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + EaDemuxContext *ea = s->priv_data; + AVIOContext *pb = s->pb; + int ret = 0; + int packet_read = 0; + int partial_packet = 0; + unsigned int chunk_type, chunk_size; + int key = 0; + int av_uninit(num_samples); + + while (!packet_read || partial_packet) { + chunk_type = avio_rl32(pb); + chunk_size = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); + if (chunk_size <= 8) + return AVERROR_INVALIDDATA; + chunk_size -= 8; + + switch (chunk_type) { + /* audio data */ + case ISNh_TAG: + /* header chunk also contains data; skip over the header portion*/ + if (chunk_size < 32) + return AVERROR_INVALIDDATA; + avio_skip(pb, 32); + chunk_size -= 32; + case ISNd_TAG: + case SCDl_TAG: + case SNDC_TAG: + case SDEN_TAG: + if (!ea->audio_codec) { + avio_skip(pb, chunk_size); + break; + } else if (ea->audio_codec == AV_CODEC_ID_PCM_S16LE_PLANAR || + ea->audio_codec == AV_CODEC_ID_MP3) { + num_samples = avio_rl32(pb); + avio_skip(pb, 8); + chunk_size -= 12; + } + if (partial_packet) { + avpriv_request_sample(s, "video header followed by audio packet"); + av_free_packet(pkt); + partial_packet = 0; + } + ret = av_get_packet(pb, pkt, chunk_size); + if (ret < 0) + return ret; + pkt->stream_index = ea->audio_stream_index; + + switch (ea->audio_codec) { + case AV_CODEC_ID_ADPCM_EA: + case AV_CODEC_ID_ADPCM_EA_R1: + case AV_CODEC_ID_ADPCM_EA_R2: + case AV_CODEC_ID_ADPCM_IMA_EA_EACS: + if (pkt->size >= 4) + pkt->duration = AV_RL32(pkt->data); + break; + case AV_CODEC_ID_ADPCM_EA_R3: + if (pkt->size >= 4) + pkt->duration = AV_RB32(pkt->data); + break; + case AV_CODEC_ID_ADPCM_IMA_EA_SEAD: + pkt->duration = ret * 2 / ea->num_channels; + break; + case AV_CODEC_ID_PCM_S16LE_PLANAR: + case AV_CODEC_ID_MP3: + pkt->duration = num_samples; + break; + default: + pkt->duration = chunk_size / (ea->bytes * ea->num_channels); + } + + packet_read = 1; + break; + + /* ending tag */ + case 0: + case ISNe_TAG: + case SCEl_TAG: + case SEND_TAG: + case SEEN_TAG: + ret = AVERROR(EIO); + packet_read = 1; + break; + + case MVIh_TAG: + case kVGT_TAG: + case pQGT_TAG: + case TGQs_TAG: + case MADk_TAG: + key = AV_PKT_FLAG_KEY; + case MVIf_TAG: + case fVGT_TAG: + case MADm_TAG: + case MADe_TAG: + avio_seek(pb, -8, SEEK_CUR); // include chunk preamble + chunk_size += 8; + goto get_video_packet; + + case mTCD_TAG: + avio_skip(pb, 8); // skip ea dct header + chunk_size -= 8; + goto get_video_packet; + + case MV0K_TAG: + case MPCh_TAG: + case pIQT_TAG: + key = AV_PKT_FLAG_KEY; + case MV0F_TAG: +get_video_packet: + if (partial_packet) { + ret = av_append_packet(pb, pkt, chunk_size); + } else + ret = av_get_packet(pb, pkt, chunk_size); + if (ret < 0) { + packet_read = 1; + break; + } + partial_packet = chunk_type == MVIh_TAG; + pkt->stream_index = ea->video_stream_index; + pkt->flags |= key; + packet_read = 1; + break; + + default: + avio_skip(pb, chunk_size); + break; + } + } + + if (ret < 0 && partial_packet) + av_free_packet(pkt); + return ret; +} + +AVInputFormat ff_ea_demuxer = { + .name = "ea", + .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts Multimedia"), + .priv_data_size = sizeof(EaDemuxContext), + .read_probe = ea_probe, + .read_header = ea_read_header, + .read_packet = ea_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/electronicarts.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/electronicarts.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/electronicarts.o libavformat/electronicarts.o: \ + libavformat/electronicarts.c libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/electronicarts.o Binary file ffmpeg/libavformat/electronicarts.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/epafdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/epafdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,104 @@ +/* + * Ensoniq Paris Audio File demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int epaf_probe(AVProbeData *p) +{ + if (((AV_RL32(p->buf) == MKTAG('f','a','p',' ') && + AV_RL32(p->buf + 8) == 1) || + (AV_RL32(p->buf) == MKTAG(' ','p','a','f') && + AV_RN32(p->buf + 8) == 0)) && + !AV_RN32(p->buf + 4) && AV_RN32(p->buf + 12) && + AV_RN32(p->buf + 20)) + return AVPROBE_SCORE_MAX / 4 * 3; + return 0; +} + +static int epaf_read_header(AVFormatContext *s) +{ + int le, sample_rate, codec, channels; + AVStream *st; + + avio_skip(s->pb, 4); + if (avio_rl32(s->pb)) + return AVERROR_INVALIDDATA; + + le = avio_rl32(s->pb); + if (le && le != 1) + return AVERROR_INVALIDDATA; + + if (le) { + sample_rate = avio_rl32(s->pb); + codec = avio_rl32(s->pb); + channels = avio_rl32(s->pb); + } else { + sample_rate = avio_rb32(s->pb); + codec = avio_rb32(s->pb); + channels = avio_rb32(s->pb); + } + + if (!channels || !sample_rate) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + switch (codec) { + case 0: + st->codec->codec_id = le ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_S16BE; + break; + case 2: + st->codec->codec_id = AV_CODEC_ID_PCM_S8; + break; + case 1: + avpriv_request_sample(s, "24-bit Paris PCM format"); + default: + return AVERROR_INVALIDDATA; + } + + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + if (avio_skip(s->pb, 2024) < 0) + return AVERROR_INVALIDDATA; + return 0; +} + +AVInputFormat ff_epaf_demuxer = { + .name = "epaf", + .long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"), + .read_probe = epaf_probe, + .read_header = epaf_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "paf,fap", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/epafdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/epafdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/epafdec.o libavformat/epafdec.o: libavformat/epafdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/epafdec.o Binary file ffmpeg/libavformat/epafdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffm.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,60 @@ +/* + * FFM (ffserver live feed) common header + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_FFM_H +#define AVFORMAT_FFM_H + +#include +#include "avformat.h" +#include "avio.h" + +/* The FFM file is made of blocks of fixed size */ +#define FFM_HEADER_SIZE 14 +#define FFM_PACKET_SIZE 4096 +#define PACKET_ID 0x666d + +/* each packet contains frames (which can span several packets */ +#define FRAME_HEADER_SIZE 16 +#define FLAG_KEY_FRAME 0x01 +#define FLAG_DTS 0x02 + +enum { + READ_HEADER, + READ_DATA, +}; + +typedef struct FFMContext { + /* only reading mode */ + int64_t write_index, file_size; + int read_state; + uint8_t header[FRAME_HEADER_SIZE+4]; + + /* read and write */ + int first_packet; /* true if first packet, needed to set the discontinuity tag */ + int packet_size; + int frame_offset; + int64_t dts; + uint8_t *packet_ptr, *packet_end; + uint8_t packet[FFM_PACKET_SIZE]; + int64_t start_time; +} FFMContext; + +#endif /* AVFORMAT_FFM_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,639 @@ +/* + * FFM (ffserver live feed) demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "internal.h" +#include "ffm.h" +#include "avio_internal.h" + +static int ffm_is_avail_data(AVFormatContext *s, int size) +{ + FFMContext *ffm = s->priv_data; + int64_t pos, avail_size; + int len; + + len = ffm->packet_end - ffm->packet_ptr; + if (size <= len) + return 1; + pos = avio_tell(s->pb); + if (!ffm->write_index) { + if (pos == ffm->file_size) + return AVERROR_EOF; + avail_size = ffm->file_size - pos; + } else { + if (pos == ffm->write_index) { + /* exactly at the end of stream */ + return AVERROR(EAGAIN); + } else if (pos < ffm->write_index) { + avail_size = ffm->write_index - pos; + } else { + avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE); + } + } + avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len; + if (size <= avail_size) + return 1; + else + return AVERROR(EAGAIN); +} + +static int ffm_resync(AVFormatContext *s, int state) +{ + av_log(s, AV_LOG_ERROR, "resyncing\n"); + while (state != PACKET_ID) { + if (url_feof(s->pb)) { + av_log(s, AV_LOG_ERROR, "cannot find FFM syncword\n"); + return -1; + } + state = (state << 8) | avio_r8(s->pb); + } + return 0; +} + +/* first is true if we read the frame header */ +static int ffm_read_data(AVFormatContext *s, + uint8_t *buf, int size, int header) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int len, fill_size, size1, frame_offset, id; + + size1 = size; + while (size > 0) { + redo: + len = ffm->packet_end - ffm->packet_ptr; + if (len < 0) + return -1; + if (len > size) + len = size; + if (len == 0) { + if (avio_tell(pb) == ffm->file_size) + avio_seek(pb, ffm->packet_size, SEEK_SET); + retry_read: + if (pb->buffer_size != ffm->packet_size) { + int64_t tell = avio_tell(pb); + ffio_set_buf_size(pb, ffm->packet_size); + avio_seek(pb, tell, SEEK_SET); + } + id = avio_rb16(pb); /* PACKET_ID */ + if (id != PACKET_ID) + if (ffm_resync(s, id) < 0) + return -1; + fill_size = avio_rb16(pb); + ffm->dts = avio_rb64(pb); + frame_offset = avio_rb16(pb); + avio_read(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE); + ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size); + if (ffm->packet_end < ffm->packet || frame_offset < 0) + return -1; + /* if first packet or resynchronization packet, we must + handle it specifically */ + if (ffm->first_packet || (frame_offset & 0x8000)) { + if (!frame_offset) { + /* This packet has no frame headers in it */ + if (avio_tell(pb) >= ffm->packet_size * 3LL) { + avio_seek(pb, -ffm->packet_size * 2LL, SEEK_CUR); + goto retry_read; + } + /* This is bad, we cannot find a valid frame header */ + return 0; + } + ffm->first_packet = 0; + if ((frame_offset & 0x7fff) < FFM_HEADER_SIZE) + return -1; + ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE; + if (!header) + break; + } else { + ffm->packet_ptr = ffm->packet; + } + goto redo; + } + memcpy(buf, ffm->packet_ptr, len); + buf += len; + ffm->packet_ptr += len; + size -= len; + header = 0; + } + return size1 - size; +} + +/* ensure that acutal seeking happens between FFM_PACKET_SIZE + and file_size - FFM_PACKET_SIZE */ +static int64_t ffm_seek1(AVFormatContext *s, int64_t pos1) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + + pos = FFMIN(pos1, ffm->file_size - FFM_PACKET_SIZE); + pos = FFMAX(pos, FFM_PACKET_SIZE); + av_dlog(s, "seek to %"PRIx64" -> %"PRIx64"\n", pos1, pos); + return avio_seek(pb, pos, SEEK_SET); +} + +static int64_t get_dts(AVFormatContext *s, int64_t pos) +{ + AVIOContext *pb = s->pb; + int64_t dts; + + ffm_seek1(s, pos); + avio_skip(pb, 4); + dts = avio_rb64(pb); + av_dlog(s, "dts=%0.6f\n", dts / 1000000.0); + return dts; +} + +static void adjust_write_index(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pts; + //int64_t orig_write_index = ffm->write_index; + int64_t pos_min, pos_max; + int64_t pts_start; + int64_t ptr = avio_tell(pb); + + + pos_min = 0; + pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE; + + pts_start = get_dts(s, pos_min); + + pts = get_dts(s, pos_max); + + if (pts - 100000 > pts_start) + goto end; + + ffm->write_index = FFM_PACKET_SIZE; + + pts_start = get_dts(s, pos_min); + + pts = get_dts(s, pos_max); + + if (pts - 100000 <= pts_start) { + while (1) { + int64_t newpos; + int64_t newpts; + + newpos = ((pos_max + pos_min) / (2 * FFM_PACKET_SIZE)) * FFM_PACKET_SIZE; + + if (newpos == pos_min) + break; + + newpts = get_dts(s, newpos); + + if (newpts - 100000 <= pts) { + pos_max = newpos; + pts = newpts; + } else { + pos_min = newpos; + } + } + ffm->write_index += pos_max; + } + + end: + avio_seek(pb, ptr, SEEK_SET); +} + + +static int ffm_close(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) + av_freep(&s->streams[i]->codec->rc_eq); + + return 0; +} + +static int ffm2_read_header(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVStream *st; + AVIOContext *pb = s->pb; + AVCodecContext *codec; + + ffm->packet_size = avio_rb32(pb); + if (ffm->packet_size != FFM_PACKET_SIZE) + goto fail; + ffm->write_index = avio_rb64(pb); + /* get also filesize */ + if (pb->seekable) { + ffm->file_size = avio_size(pb); + if (ffm->write_index && 0) + adjust_write_index(s); + } else { + ffm->file_size = (UINT64_C(1) << 63) - 1; + } + + while(!url_feof(pb)) { + unsigned id = avio_rb32(pb); + unsigned size = avio_rb32(pb); + int64_t next = avio_tell(pb) + size; + char rc_eq_buf[128]; + + if(!id) + break; + + switch(id) { + case MKBETAG('M', 'A', 'I', 'N'): + avio_rb32(pb); /* nb_streams */ + avio_rb32(pb); /* total bitrate */ + break; + case MKBETAG('C', 'O', 'M', 'M'): + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + + avpriv_set_pts_info(st, 64, 1, 1000000); + + codec = st->codec; + /* generic info */ + codec->codec_id = avio_rb32(pb); + codec->codec_type = avio_r8(pb); + codec->bit_rate = avio_rb32(pb); + codec->flags = avio_rb32(pb); + codec->flags2 = avio_rb32(pb); + codec->debug = avio_rb32(pb); + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + codec->extradata_size = avio_rb32(pb); + codec->extradata = av_malloc(codec->extradata_size); + if (!codec->extradata) + return AVERROR(ENOMEM); + avio_read(pb, codec->extradata, codec->extradata_size); + } + avio_seek(pb, next, SEEK_SET); + id = avio_rb32(pb); + size = avio_rb32(pb); + next = avio_tell(pb) + size; + switch(id) { + case MKBETAG('S', 'T', 'V', 'I'): + codec->time_base.num = avio_rb32(pb); + codec->time_base.den = avio_rb32(pb); + codec->width = avio_rb16(pb); + codec->height = avio_rb16(pb); + codec->gop_size = avio_rb16(pb); + codec->pix_fmt = avio_rb32(pb); + codec->qmin = avio_r8(pb); + codec->qmax = avio_r8(pb); + codec->max_qdiff = avio_r8(pb); + codec->qcompress = avio_rb16(pb) / 10000.0; + codec->qblur = avio_rb16(pb) / 10000.0; + codec->bit_rate_tolerance = avio_rb32(pb); + avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf)); + codec->rc_eq = av_strdup(rc_eq_buf); + codec->rc_max_rate = avio_rb32(pb); + codec->rc_min_rate = avio_rb32(pb); + codec->rc_buffer_size = avio_rb32(pb); + codec->i_quant_factor = av_int2double(avio_rb64(pb)); + codec->b_quant_factor = av_int2double(avio_rb64(pb)); + codec->i_quant_offset = av_int2double(avio_rb64(pb)); + codec->b_quant_offset = av_int2double(avio_rb64(pb)); + codec->dct_algo = avio_rb32(pb); + codec->strict_std_compliance = avio_rb32(pb); + codec->max_b_frames = avio_rb32(pb); + codec->mpeg_quant = avio_rb32(pb); + codec->intra_dc_precision = avio_rb32(pb); + codec->me_method = avio_rb32(pb); + codec->mb_decision = avio_rb32(pb); + codec->nsse_weight = avio_rb32(pb); + codec->frame_skip_cmp = avio_rb32(pb); + codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb)); + codec->codec_tag = avio_rb32(pb); + codec->thread_count = avio_r8(pb); + codec->coder_type = avio_rb32(pb); + codec->me_cmp = avio_rb32(pb); + codec->me_subpel_quality = avio_rb32(pb); + codec->me_range = avio_rb32(pb); + codec->keyint_min = avio_rb32(pb); + codec->scenechange_threshold = avio_rb32(pb); + codec->b_frame_strategy = avio_rb32(pb); + codec->qcompress = av_int2double(avio_rb64(pb)); + codec->qblur = av_int2double(avio_rb64(pb)); + codec->max_qdiff = avio_rb32(pb); + codec->refs = avio_rb32(pb); + break; + case MKBETAG('S', 'T', 'A', 'U'): + codec->sample_rate = avio_rb32(pb); + codec->channels = avio_rl16(pb); + codec->frame_size = avio_rl16(pb); + break; + } + break; + } + avio_seek(pb, next, SEEK_SET); + } + + /* get until end of block reached */ + while ((avio_tell(pb) % ffm->packet_size) != 0) + avio_r8(pb); + + /* init packet demux */ + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet; + ffm->frame_offset = 0; + ffm->dts = 0; + ffm->read_state = READ_HEADER; + ffm->first_packet = 1; + return 0; + fail: + ffm_close(s); + return -1; +} + +static int ffm_read_header(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVStream *st; + AVIOContext *pb = s->pb; + AVCodecContext *codec; + int i, nb_streams; + uint32_t tag; + + /* header */ + tag = avio_rl32(pb); + if (tag == MKTAG('F', 'F', 'M', '2')) + return ffm2_read_header(s); + if (tag != MKTAG('F', 'F', 'M', '1')) + goto fail; + ffm->packet_size = avio_rb32(pb); + if (ffm->packet_size != FFM_PACKET_SIZE) + goto fail; + ffm->write_index = avio_rb64(pb); + /* get also filesize */ + if (pb->seekable) { + ffm->file_size = avio_size(pb); + if (ffm->write_index && 0) + adjust_write_index(s); + } else { + ffm->file_size = (UINT64_C(1) << 63) - 1; + } + + nb_streams = avio_rb32(pb); + avio_rb32(pb); /* total bitrate */ + /* read each stream */ + for(i=0;icodec; + /* generic info */ + codec->codec_id = avio_rb32(pb); + codec->codec_type = avio_r8(pb); /* codec_type */ + codec->bit_rate = avio_rb32(pb); + codec->flags = avio_rb32(pb); + codec->flags2 = avio_rb32(pb); + codec->debug = avio_rb32(pb); + /* specific info */ + switch(codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + codec->time_base.num = avio_rb32(pb); + codec->time_base.den = avio_rb32(pb); + codec->width = avio_rb16(pb); + codec->height = avio_rb16(pb); + codec->gop_size = avio_rb16(pb); + codec->pix_fmt = avio_rb32(pb); + codec->qmin = avio_r8(pb); + codec->qmax = avio_r8(pb); + codec->max_qdiff = avio_r8(pb); + codec->qcompress = avio_rb16(pb) / 10000.0; + codec->qblur = avio_rb16(pb) / 10000.0; + codec->bit_rate_tolerance = avio_rb32(pb); + avio_get_str(pb, INT_MAX, rc_eq_buf, sizeof(rc_eq_buf)); + codec->rc_eq = av_strdup(rc_eq_buf); + codec->rc_max_rate = avio_rb32(pb); + codec->rc_min_rate = avio_rb32(pb); + codec->rc_buffer_size = avio_rb32(pb); + codec->i_quant_factor = av_int2double(avio_rb64(pb)); + codec->b_quant_factor = av_int2double(avio_rb64(pb)); + codec->i_quant_offset = av_int2double(avio_rb64(pb)); + codec->b_quant_offset = av_int2double(avio_rb64(pb)); + codec->dct_algo = avio_rb32(pb); + codec->strict_std_compliance = avio_rb32(pb); + codec->max_b_frames = avio_rb32(pb); + codec->mpeg_quant = avio_rb32(pb); + codec->intra_dc_precision = avio_rb32(pb); + codec->me_method = avio_rb32(pb); + codec->mb_decision = avio_rb32(pb); + codec->nsse_weight = avio_rb32(pb); + codec->frame_skip_cmp = avio_rb32(pb); + codec->rc_buffer_aggressivity = av_int2double(avio_rb64(pb)); + codec->codec_tag = avio_rb32(pb); + codec->thread_count = avio_r8(pb); + codec->coder_type = avio_rb32(pb); + codec->me_cmp = avio_rb32(pb); + codec->me_subpel_quality = avio_rb32(pb); + codec->me_range = avio_rb32(pb); + codec->keyint_min = avio_rb32(pb); + codec->scenechange_threshold = avio_rb32(pb); + codec->b_frame_strategy = avio_rb32(pb); + codec->qcompress = av_int2double(avio_rb64(pb)); + codec->qblur = av_int2double(avio_rb64(pb)); + codec->max_qdiff = avio_rb32(pb); + codec->refs = avio_rb32(pb); + break; + case AVMEDIA_TYPE_AUDIO: + codec->sample_rate = avio_rb32(pb); + codec->channels = avio_rl16(pb); + codec->frame_size = avio_rl16(pb); + break; + default: + goto fail; + } + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + codec->extradata_size = avio_rb32(pb); + codec->extradata = av_malloc(codec->extradata_size); + if (!codec->extradata) + return AVERROR(ENOMEM); + avio_read(pb, codec->extradata, codec->extradata_size); + } + } + + /* get until end of block reached */ + while ((avio_tell(pb) % ffm->packet_size) != 0) + avio_r8(pb); + + /* init packet demux */ + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet; + ffm->frame_offset = 0; + ffm->dts = 0; + ffm->read_state = READ_HEADER; + ffm->first_packet = 1; + return 0; + fail: + ffm_close(s); + return -1; +} + +/* return < 0 if eof */ +static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size; + FFMContext *ffm = s->priv_data; + int duration, ret; + + switch(ffm->read_state) { + case READ_HEADER: + if ((ret = ffm_is_avail_data(s, FRAME_HEADER_SIZE+4)) < 0) + return ret; + + av_dlog(s, "pos=%08"PRIx64" spos=%"PRIx64", write_index=%"PRIx64" size=%"PRIx64"\n", + avio_tell(s->pb), s->pb->pos, ffm->write_index, ffm->file_size); + if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) != + FRAME_HEADER_SIZE) + return -1; + if (ffm->header[1] & FLAG_DTS) + if (ffm_read_data(s, ffm->header+16, 4, 1) != 4) + return -1; + ffm->read_state = READ_DATA; + /* fall thru */ + case READ_DATA: + size = AV_RB24(ffm->header + 2); + if ((ret = ffm_is_avail_data(s, size)) < 0) + return ret; + + duration = AV_RB24(ffm->header + 5); + + if (av_new_packet(pkt, size) < 0) { + return AVERROR(ENOMEM); + } + pkt->stream_index = ffm->header[0]; + if ((unsigned)pkt->stream_index >= s->nb_streams) { + av_log(s, AV_LOG_ERROR, "invalid stream index %d\n", pkt->stream_index); + av_free_packet(pkt); + ffm->read_state = READ_HEADER; + return -1; + } + pkt->pos = avio_tell(s->pb); + if (ffm->header[1] & FLAG_KEY_FRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + + ffm->read_state = READ_HEADER; + if (ffm_read_data(s, pkt->data, size, 0) != size) { + /* bad case: desynchronized packet. we cancel all the packet loading */ + av_free_packet(pkt); + return -1; + } + pkt->pts = AV_RB64(ffm->header+8); + if (ffm->header[1] & FLAG_DTS) + pkt->dts = pkt->pts - AV_RB32(ffm->header+16); + else + pkt->dts = pkt->pts; + pkt->duration = duration; + break; + } + return 0; +} + +/* seek to a given time in the file. The file read pointer is + positioned at or before pts. XXX: the following code is quite + approximative */ +static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, int flags) +{ + FFMContext *ffm = s->priv_data; + int64_t pos_min, pos_max, pos; + int64_t pts_min, pts_max, pts; + double pos1; + + av_dlog(s, "wanted_pts=%0.6f\n", wanted_pts / 1000000.0); + /* find the position using linear interpolation (better than + dichotomy in typical cases) */ + if (ffm->write_index && ffm->write_index < ffm->file_size) { + if (get_dts(s, FFM_PACKET_SIZE) < wanted_pts) { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->write_index - FFM_PACKET_SIZE; + } else { + pos_min = ffm->write_index; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } + } else { + pos_min = FFM_PACKET_SIZE; + pos_max = ffm->file_size - FFM_PACKET_SIZE; + } + while (pos_min <= pos_max) { + pts_min = get_dts(s, pos_min); + pts_max = get_dts(s, pos_max); + if (pts_min > wanted_pts || pts_max <= wanted_pts) { + pos = pts_min > wanted_pts ? pos_min : pos_max; + goto found; + } + /* linear interpolation */ + pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) / + (double)(pts_max - pts_min); + pos = (((int64_t)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE; + if (pos <= pos_min) + pos = pos_min; + else if (pos >= pos_max) + pos = pos_max; + pts = get_dts(s, pos); + /* check if we are lucky */ + if (pts == wanted_pts) { + goto found; + } else if (pts > wanted_pts) { + pos_max = pos - FFM_PACKET_SIZE; + } else { + pos_min = pos + FFM_PACKET_SIZE; + } + } + pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; + + found: + if (ffm_seek1(s, pos) < 0) + return -1; + + /* reset read state */ + ffm->read_state = READ_HEADER; + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet; + ffm->first_packet = 1; + + return 0; +} + +static int ffm_probe(AVProbeData *p) +{ + if ( + p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' && + (p->buf[3] == '1' || p->buf[3] == '2')) + return AVPROBE_SCORE_MAX + 1; + return 0; +} + +AVInputFormat ff_ffm_demuxer = { + .name = "ffm", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), + .priv_data_size = sizeof(FFMContext), + .read_probe = ffm_probe, + .read_header = ffm_read_header, + .read_packet = ffm_read_packet, + .read_close = ffm_close, + .read_seek = ffm_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/ffmdec.o libavformat/ffmdec.o: libavformat/ffmdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/ffm.h libavformat/avio_internal.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmdec.o Binary file ffmpeg/libavformat/ffmdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,280 @@ +/* + * FFM (ffserver live feed) muxer + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/avassert.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#include "ffm.h" + +static void flush_packet(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + int fill_size, h; + AVIOContext *pb = s->pb; + + fill_size = ffm->packet_end - ffm->packet_ptr; + memset(ffm->packet_ptr, 0, fill_size); + + av_assert1(avio_tell(pb) % ffm->packet_size == 0); + + /* put header */ + avio_wb16(pb, PACKET_ID); + avio_wb16(pb, fill_size); + avio_wb64(pb, ffm->dts); + h = ffm->frame_offset; + if (ffm->first_packet) + h |= 0x8000; + avio_wb16(pb, h); + avio_write(pb, ffm->packet, ffm->packet_end - ffm->packet); + avio_flush(pb); + + /* prepare next packet */ + ffm->frame_offset = 0; /* no key frame */ + ffm->packet_ptr = ffm->packet; + ffm->first_packet = 0; +} + +/* 'first' is true if first data of a frame */ +static void ffm_write_data(AVFormatContext *s, + const uint8_t *buf, int size, + int64_t dts, int header) +{ + FFMContext *ffm = s->priv_data; + int len; + + if (header && ffm->frame_offset == 0) { + ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE; + ffm->dts = dts; + } + + /* write as many packets as needed */ + while (size > 0) { + len = ffm->packet_end - ffm->packet_ptr; + if (len > size) + len = size; + memcpy(ffm->packet_ptr, buf, len); + + ffm->packet_ptr += len; + buf += len; + size -= len; + if (ffm->packet_ptr >= ffm->packet_end) + flush_packet(s); + } +} + +static void write_header_chunk(AVIOContext *pb, AVIOContext *dpb, unsigned id) +{ + uint8_t *dyn_buf; + int dyn_size= avio_close_dyn_buf(dpb, &dyn_buf); + avio_wb32(pb, id); + avio_wb32(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + av_free(dyn_buf); +} + +static int ffm_write_header(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + AVDictionaryEntry *t; + AVStream *st; + AVIOContext *pb = s->pb; + AVCodecContext *codec; + int bit_rate, i; + + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + int ret = av_parse_time(&ffm->start_time, t->value, 0); + if (ret < 0) + return ret; + } + + ffm->packet_size = FFM_PACKET_SIZE; + + /* header */ + avio_wl32(pb, MKTAG('F', 'F', 'M', '2')); + avio_wb32(pb, ffm->packet_size); + avio_wb64(pb, 0); /* current write position */ + + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); + + avio_wb32(pb, s->nb_streams); + bit_rate = 0; + for(i=0;inb_streams;i++) { + st = s->streams[i]; + bit_rate += st->codec->bit_rate; + } + avio_wb32(pb, bit_rate); + + write_header_chunk(s->pb, pb, MKBETAG('M', 'A', 'I', 'N')); + + /* list of streams */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + avpriv_set_pts_info(st, 64, 1, 1000000); + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); + + codec = st->codec; + /* generic info */ + avio_wb32(pb, codec->codec_id); + avio_w8(pb, codec->codec_type); + avio_wb32(pb, codec->bit_rate); + avio_wb32(pb, codec->flags); + avio_wb32(pb, codec->flags2); + avio_wb32(pb, codec->debug); + if (codec->flags & CODEC_FLAG_GLOBAL_HEADER) { + avio_wb32(pb, codec->extradata_size); + avio_write(pb, codec->extradata, codec->extradata_size); + } + write_header_chunk(s->pb, pb, MKBETAG('C', 'O', 'M', 'M')); + if(avio_open_dyn_buf(&pb) < 0) + return AVERROR(ENOMEM); + /* specific info */ + switch(codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + avio_wb32(pb, codec->time_base.num); + avio_wb32(pb, codec->time_base.den); + avio_wb16(pb, codec->width); + avio_wb16(pb, codec->height); + avio_wb16(pb, codec->gop_size); + avio_wb32(pb, codec->pix_fmt); + avio_w8(pb, codec->qmin); + avio_w8(pb, codec->qmax); + avio_w8(pb, codec->max_qdiff); + avio_wb16(pb, (int) (codec->qcompress * 10000.0)); + avio_wb16(pb, (int) (codec->qblur * 10000.0)); + avio_wb32(pb, codec->bit_rate_tolerance); + avio_put_str(pb, codec->rc_eq ? codec->rc_eq : "tex^qComp"); + avio_wb32(pb, codec->rc_max_rate); + avio_wb32(pb, codec->rc_min_rate); + avio_wb32(pb, codec->rc_buffer_size); + avio_wb64(pb, av_double2int(codec->i_quant_factor)); + avio_wb64(pb, av_double2int(codec->b_quant_factor)); + avio_wb64(pb, av_double2int(codec->i_quant_offset)); + avio_wb64(pb, av_double2int(codec->b_quant_offset)); + avio_wb32(pb, codec->dct_algo); + avio_wb32(pb, codec->strict_std_compliance); + avio_wb32(pb, codec->max_b_frames); + avio_wb32(pb, codec->mpeg_quant); + avio_wb32(pb, codec->intra_dc_precision); + avio_wb32(pb, codec->me_method); + avio_wb32(pb, codec->mb_decision); + avio_wb32(pb, codec->nsse_weight); + avio_wb32(pb, codec->frame_skip_cmp); + avio_wb64(pb, av_double2int(codec->rc_buffer_aggressivity)); + avio_wb32(pb, codec->codec_tag); + avio_w8(pb, codec->thread_count); + avio_wb32(pb, codec->coder_type); + avio_wb32(pb, codec->me_cmp); + avio_wb32(pb, codec->me_subpel_quality); + avio_wb32(pb, codec->me_range); + avio_wb32(pb, codec->keyint_min); + avio_wb32(pb, codec->scenechange_threshold); + avio_wb32(pb, codec->b_frame_strategy); + avio_wb64(pb, av_double2int(codec->qcompress)); + avio_wb64(pb, av_double2int(codec->qblur)); + avio_wb32(pb, codec->max_qdiff); + avio_wb32(pb, codec->refs); + write_header_chunk(s->pb, pb, MKBETAG('S', 'T', 'V', 'I')); + break; + case AVMEDIA_TYPE_AUDIO: + avio_wb32(pb, codec->sample_rate); + avio_wl16(pb, codec->channels); + avio_wl16(pb, codec->frame_size); + write_header_chunk(s->pb, pb, MKBETAG('S', 'T', 'A', 'U')); + break; + default: + return -1; + } + } + pb = s->pb; + + avio_wb64(pb, 0); // end of header + + /* flush until end of block reached */ + while ((avio_tell(pb) % ffm->packet_size) != 0) + avio_w8(pb, 0); + + avio_flush(pb); + + /* init packet mux */ + ffm->packet_ptr = ffm->packet; + ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE; + av_assert0(ffm->packet_end >= ffm->packet); + ffm->frame_offset = 0; + ffm->dts = 0; + ffm->first_packet = 1; + + return 0; +} + +static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + FFMContext *ffm = s->priv_data; + int64_t dts; + uint8_t header[FRAME_HEADER_SIZE+4]; + int header_size = FRAME_HEADER_SIZE; + + dts = ffm->start_time + pkt->dts; + /* packet size & key_frame */ + header[0] = pkt->stream_index; + header[1] = 0; + if (pkt->flags & AV_PKT_FLAG_KEY) + header[1] |= FLAG_KEY_FRAME; + AV_WB24(header+2, pkt->size); + AV_WB24(header+5, pkt->duration); + AV_WB64(header+8, ffm->start_time + pkt->pts); + if (pkt->pts != pkt->dts) { + header[1] |= FLAG_DTS; + AV_WB32(header+16, pkt->pts - pkt->dts); + header_size += 4; + } + ffm_write_data(s, header, header_size, dts, 1); + ffm_write_data(s, pkt->data, pkt->size, dts, 0); + + return 0; +} + +static int ffm_write_trailer(AVFormatContext *s) +{ + FFMContext *ffm = s->priv_data; + + /* flush packets */ + if (ffm->packet_ptr > ffm->packet) + flush_packet(s); + + return 0; +} + +AVOutputFormat ff_ffm_muxer = { + .name = "ffm", + .long_name = NULL_IF_CONFIG_SMALL("FFM (FFserver live feed)"), + .extensions = "ffm", + .priv_data_size = sizeof(FFMContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_header = ffm_write_header, + .write_packet = ffm_write_packet, + .write_trailer = ffm_write_trailer, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/ffmenc.o libavformat/ffmenc.o: libavformat/ffmenc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/intfloat.h libavutil/avassert.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/parseutils.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/ffm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmenc.o Binary file ffmpeg/libavformat/ffmenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmeta.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmeta.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,29 @@ +/* + * Common data for metadata muxer/demuxer + * Copyright (c) 2010 Anton Khirnov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_FFMETA_H +#define AVFORMAT_FFMETA_H + +#define ID_STRING ";FFMETADATA" +#define ID_CHAPTER "[CHAPTER]" +#define ID_STREAM "[STREAM]" + +#endif /* AVFORMAT_FFMETA_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetadec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmetadec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,175 @@ +/* + * Metadata demuxer + * Copyright (c) 2010 Anton Khirnov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "ffmeta.h" +#include "internal.h" +#include "libavutil/dict.h" + +static int probe(AVProbeData *p) +{ + if(!memcmp(p->buf, ID_STRING, strlen(ID_STRING))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static void get_line(AVIOContext *s, uint8_t *buf, int size) +{ + do { + uint8_t c; + int i = 0; + + while ((c = avio_r8(s))) { + if (c == '\\') { + if (i < size - 1) + buf[i++] = c; + c = avio_r8(s); + } else if (c == '\n') + break; + + if (i < size - 1) + buf[i++] = c; + } + buf[i] = 0; + } while (!url_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0)); +} + +static AVChapter *read_chapter(AVFormatContext *s) +{ + uint8_t line[256]; + int64_t start, end; + AVRational tb = {1, 1e9}; + + get_line(s->pb, line, sizeof(line)); + + if (sscanf(line, "TIMEBASE=%d/%d", &tb.num, &tb.den)) + get_line(s->pb, line, sizeof(line)); + if (!sscanf(line, "START=%"SCNd64, &start)) { + av_log(s, AV_LOG_ERROR, "Expected chapter start timestamp, found %s.\n", line); + start = (s->nb_chapters && s->chapters[s->nb_chapters - 1]->end != AV_NOPTS_VALUE) ? + s->chapters[s->nb_chapters - 1]->end : 0; + } else + get_line(s->pb, line, sizeof(line)); + + if (!sscanf(line, "END=%"SCNd64, &end)) { + av_log(s, AV_LOG_ERROR, "Expected chapter end timestamp, found %s.\n", line); + end = AV_NOPTS_VALUE; + } + + return avpriv_new_chapter(s, s->nb_chapters, tb, start, end, NULL); +} + +static uint8_t *unescape(uint8_t *buf, int size) +{ + uint8_t *ret = av_malloc(size + 1); + uint8_t *p1 = ret, *p2 = buf; + + if (!ret) + return NULL; + + while (p2 < buf + size) { + if (*p2 == '\\') + p2++; + *p1++ = *p2++; + } + *p1 = 0; + return ret; +} + +static int read_tag(uint8_t *line, AVDictionary **m) +{ + uint8_t *key, *value, *p = line; + + /* find first not escaped '=' */ + while (1) { + if (*p == '=') + break; + else if (*p == '\\') + p++; + + if (*p++) + continue; + + return 0; + } + + if (!(key = unescape(line, p - line))) + return AVERROR(ENOMEM); + if (!(value = unescape(p + 1, strlen(p + 1)))) { + av_free(key); + return AVERROR(ENOMEM); + } + + av_dict_set(m, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + return 0; +} + +static int read_header(AVFormatContext *s) +{ + AVDictionary **m = &s->metadata; + uint8_t line[1024]; + + while(!url_feof(s->pb)) { + get_line(s->pb, line, sizeof(line)); + + if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) { + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return -1; + + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_FFMETADATA; + + m = &st->metadata; + } else if (!memcmp(line, ID_CHAPTER, strlen(ID_CHAPTER))) { + AVChapter *ch = read_chapter(s); + + if (!ch) + return -1; + + m = &ch->metadata; + } else + read_tag(line, m); + } + + s->start_time = 0; + if (s->nb_chapters) + s->duration = av_rescale_q(s->chapters[s->nb_chapters - 1]->end, + s->chapters[s->nb_chapters - 1]->time_base, + AV_TIME_BASE_Q); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + return AVERROR_EOF; +} + +AVInputFormat ff_ffmetadata_demuxer = { + .name = "ffmetadata", + .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetadec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmetadec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/ffmetadec.o libavformat/ffmetadec.o: libavformat/ffmetadec.c \ + libavutil/mathematics.h libavutil/attributes.h libavutil/rational.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/ffmeta.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetadec.o Binary file ffmpeg/libavformat/ffmetadec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetaenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmetaenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,99 @@ +/* + * Metadata muxer + * Copyright (c) 2010 Anton Khirnov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "avformat.h" +#include "ffmeta.h" +#include "libavutil/dict.h" + + +static void write_escape_str(AVIOContext *s, const uint8_t *str) +{ + const uint8_t *p = str; + + while (*p) { + if (*p == '#' || *p == ';' || *p == '=' || *p == '\\' || *p == '\n') + avio_w8(s, '\\'); + avio_w8(s, *p); + p++; + } +} + +static void write_tags(AVIOContext *s, AVDictionary *m) +{ + AVDictionaryEntry *t = NULL; + while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) { + write_escape_str(s, t->key); + avio_w8(s, '='); + write_escape_str(s, t->value); + avio_w8(s, '\n'); + } +} + +static int write_header(AVFormatContext *s) +{ + avio_write(s->pb, ID_STRING, sizeof(ID_STRING) - 1); + avio_w8(s->pb, '1'); // version + avio_w8(s->pb, '\n'); + avio_flush(s->pb); + return 0; +} + +static int write_trailer(AVFormatContext *s) +{ + int i; + + write_tags(s->pb, s->metadata); + + for (i = 0; i < s->nb_streams; i++) { + avio_write(s->pb, ID_STREAM, sizeof(ID_STREAM) - 1); + avio_w8(s->pb, '\n'); + write_tags(s->pb, s->streams[i]->metadata); + } + + for (i = 0; i < s->nb_chapters; i++) { + AVChapter *ch = s->chapters[i]; + avio_write(s->pb, ID_CHAPTER, sizeof(ID_CHAPTER) - 1); + avio_w8(s->pb, '\n'); + avio_printf(s->pb, "TIMEBASE=%d/%d\n", ch->time_base.num, ch->time_base.den); + avio_printf(s->pb, "START=%"PRId64"\n", ch->start); + avio_printf(s->pb, "END=%"PRId64"\n", ch->end); + write_tags(s->pb, ch->metadata); + } + + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + return 0; +} + +AVOutputFormat ff_ffmetadata_muxer = { + .name = "ffmetadata", + .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), + .extensions = "ffmeta", + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, + .flags = AVFMT_NOTIMESTAMPS | AVFMT_NOSTREAMS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetaenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ffmetaenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/ffmetaenc.o libavformat/ffmetaenc.o: libavformat/ffmetaenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/ffmeta.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ffmetaenc.o Binary file ffmpeg/libavformat/ffmetaenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/file.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/file.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,218 @@ +/* + * buffered file I/O + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include +#if HAVE_IO_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include "os_support.h" +#include "url.h" + +/* Some systems may not have S_ISFIFO */ +#ifndef S_ISFIFO +# ifdef S_IFIFO +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) 0 +# endif +#endif + +/* standard file protocol */ + +typedef struct FileContext { + const AVClass *class; + int fd; + int trunc; +} FileContext; + +static const AVOption file_options[] = { + { "truncate", "Truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + +static const AVClass file_class = { + .class_name = "file", + .item_name = av_default_item_name, + .option = file_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int file_read(URLContext *h, unsigned char *buf, int size) +{ + FileContext *c = h->priv_data; + int r = read(c->fd, buf, size); + return (-1 == r)?AVERROR(errno):r; +} + +static int file_write(URLContext *h, const unsigned char *buf, int size) +{ + FileContext *c = h->priv_data; + int r = write(c->fd, buf, size); + return (-1 == r)?AVERROR(errno):r; +} + +static int file_get_handle(URLContext *h) +{ + FileContext *c = h->priv_data; + return c->fd; +} + +static int file_check(URLContext *h, int mask) +{ +#if HAVE_ACCESS && defined(R_OK) + int ret = 0; + if (access(h->filename, F_OK) < 0) + return AVERROR(errno); + if (mask&AVIO_FLAG_READ) + if (access(h->filename, R_OK) >= 0) + ret |= AVIO_FLAG_READ; + if (mask&AVIO_FLAG_WRITE) + if (access(h->filename, W_OK) >= 0) + ret |= AVIO_FLAG_WRITE; +#else + struct stat st; + int ret = stat(h->filename, &st); + if (ret < 0) + return AVERROR(errno); + + ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0; + ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0; +#endif + return ret; +} + +#if CONFIG_FILE_PROTOCOL + +static int file_open(URLContext *h, const char *filename, int flags) +{ + FileContext *c = h->priv_data; + int access; + int fd; + struct stat st; + + av_strstart(filename, "file:", &filename); + + if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { + access = O_CREAT | O_RDWR; + if (c->trunc) + access |= O_TRUNC; + } else if (flags & AVIO_FLAG_WRITE) { + access = O_CREAT | O_WRONLY; + if (c->trunc) + access |= O_TRUNC; + } else { + access = O_RDONLY; + } +#ifdef O_BINARY + access |= O_BINARY; +#endif + fd = open(filename, access, 0666); + if (fd == -1) + return AVERROR(errno); + c->fd = fd; + + h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode); + + return 0; +} + +/* XXX: use llseek */ +static int64_t file_seek(URLContext *h, int64_t pos, int whence) +{ + FileContext *c = h->priv_data; + int64_t ret; + + if (whence == AVSEEK_SIZE) { + struct stat st; + ret = fstat(c->fd, &st); + return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size); + } + + ret = lseek(c->fd, pos, whence); + + return ret < 0 ? AVERROR(errno) : ret; +} + +static int file_close(URLContext *h) +{ + FileContext *c = h->priv_data; + return close(c->fd); +} + +URLProtocol ff_file_protocol = { + .name = "file", + .url_open = file_open, + .url_read = file_read, + .url_write = file_write, + .url_seek = file_seek, + .url_close = file_close, + .url_get_file_handle = file_get_handle, + .url_check = file_check, + .priv_data_size = sizeof(FileContext), + .priv_data_class = &file_class, +}; + +#endif /* CONFIG_FILE_PROTOCOL */ + +#if CONFIG_PIPE_PROTOCOL + +static int pipe_open(URLContext *h, const char *filename, int flags) +{ + FileContext *c = h->priv_data; + int fd; + char *final; + av_strstart(filename, "pipe:", &filename); + + fd = strtol(filename, &final, 10); + if((filename == final) || *final ) {/* No digits found, or something like 10ab */ + if (flags & AVIO_FLAG_WRITE) { + fd = 1; + } else { + fd = 0; + } + } +#if HAVE_SETMODE + setmode(fd, O_BINARY); +#endif + c->fd = fd; + h->is_streamed = 1; + return 0; +} + +URLProtocol ff_pipe_protocol = { + .name = "pipe", + .url_open = pipe_open, + .url_read = file_read, + .url_write = file_write, + .url_get_file_handle = file_get_handle, + .url_check = file_check, + .priv_data_size = sizeof(FileContext), +}; + +#endif /* CONFIG_PIPE_PROTOCOL */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/file.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/file.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/file.o libavformat/file.o: libavformat/file.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/opt.h \ + libavutil/rational.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/os_support.h \ + config.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/file.o Binary file ffmpeg/libavformat/file.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/filmstripdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,110 @@ +/* + * Adobe Filmstrip demuxer + * Copyright (c) 2010 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Adobe Filmstrip demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define RAND_TAG MKBETAG('R','a','n','d') + +typedef struct { + int leading; +} FilmstripDemuxContext; + +static int read_header(AVFormatContext *s) +{ + FilmstripDemuxContext *film = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + + if (!s->pb->seekable) + return AVERROR(EIO); + + avio_seek(pb, avio_size(pb) - 36, SEEK_SET); + if (avio_rb32(pb) != RAND_TAG) { + av_log(s, AV_LOG_ERROR, "magic number not found\n"); + return AVERROR_INVALIDDATA; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->nb_frames = avio_rb32(pb); + if (avio_rb16(pb) != 0) { + avpriv_request_sample(s, "Unsupported packing method"); + return AVERROR_PATCHWELCOME; + } + + avio_skip(pb, 2); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->pix_fmt = AV_PIX_FMT_RGBA; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + film->leading = avio_rb16(pb); + avpriv_set_pts_info(st, 64, 1, avio_rb16(pb)); + + avio_seek(pb, 0, SEEK_SET); + + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + FilmstripDemuxContext *film = s->priv_data; + AVStream *st = s->streams[0]; + + if (url_feof(s->pb)) + return AVERROR(EIO); + pkt->dts = avio_tell(s->pb) / (st->codec->width * (st->codec->height + film->leading) * 4); + pkt->size = av_get_packet(s->pb, pkt, st->codec->width * st->codec->height * 4); + avio_skip(s->pb, st->codec->width * film->leading * 4); + if (pkt->size < 0) + return pkt->size; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVStream *st = s->streams[stream_index]; + if (avio_seek(s->pb, FFMAX(timestamp, 0) * st->codec->width * st->codec->height * 4, SEEK_SET) < 0) + return -1; + return 0; +} + +AVInputFormat ff_filmstrip_demuxer = { + .name = "filmstrip", + .long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), + .priv_data_size = sizeof(FilmstripDemuxContext), + .read_header = read_header, + .read_packet = read_packet, + .read_seek = read_seek, + .extensions = "flm", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/filmstripdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/filmstripdec.o libavformat/filmstripdec.o: \ + libavformat/filmstripdec.c libavutil/intreadwrite.h libavutil/avconfig.h \ + libavutil/attributes.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripdec.o Binary file ffmpeg/libavformat/filmstripdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/filmstripenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,84 @@ +/* + * Adobe Filmstrip muxer + * Copyright (c) 2010 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Adobe Filmstrip muxer + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +#define RAND_TAG MKBETAG('R','a','n','d') + +typedef struct { + int nb_frames; +} FilmstripMuxContext; + +static int write_header(AVFormatContext *s) +{ + if (s->streams[0]->codec->pix_fmt != AV_PIX_FMT_RGBA) { + av_log(s, AV_LOG_ERROR, "only AV_PIX_FMT_RGBA is supported\n"); + return AVERROR_INVALIDDATA; + } + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + FilmstripMuxContext *film = s->priv_data; + avio_write(s->pb, pkt->data, pkt->size); + film->nb_frames++; + return 0; +} + +static int write_trailer(AVFormatContext *s) +{ + FilmstripMuxContext *film = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int i; + + avio_wb32(pb, RAND_TAG); + avio_wb32(pb, film->nb_frames); + avio_wb16(pb, 0); // packing method + avio_wb16(pb, 0); // reserved + avio_wb16(pb, st->codec->width); + avio_wb16(pb, st->codec->height); + avio_wb16(pb, 0); // leading + avio_wb16(pb, st->codec->time_base.den / st->codec->time_base.num); + for (i = 0; i < 16; i++) + avio_w8(pb, 0x00); // reserved + + return 0; +} + +AVOutputFormat ff_filmstrip_muxer = { + .name = "filmstrip", + .long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), + .extensions = "flm", + .priv_data_size = sizeof(FilmstripMuxContext), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/filmstripenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/filmstripenc.o libavformat/filmstripenc.o: \ + libavformat/filmstripenc.c libavutil/intreadwrite.h libavutil/avconfig.h \ + libavutil/attributes.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/filmstripenc.o Binary file ffmpeg/libavformat/filmstripenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,295 @@ +/* + * Raw FLAC demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/flac.h" +#include "avformat.h" +#include "id3v2.h" +#include "internal.h" +#include "rawdec.h" +#include "oggdec.h" +#include "vorbiscomment.h" +#include "libavcodec/bytestream.h" + +#define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0) + +static int parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) +{ + const CodecMime *mime = ff_id3v2_mime_tags; + enum AVCodecID id = AV_CODEC_ID_NONE; + AVBufferRef *data = NULL; + uint8_t mimetype[64], *desc = NULL; + AVIOContext *pb = NULL; + AVStream *st; + int type, width, height; + int len, ret = 0; + + pb = avio_alloc_context(buf, buf_size, 0, NULL, NULL, NULL, NULL); + if (!pb) + return AVERROR(ENOMEM); + + /* read the picture type */ + type = avio_rb32(pb); + if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types) || type < 0) { + av_log(s, AV_LOG_ERROR, "Invalid picture type: %d.\n", type); + if (s->error_recognition & AV_EF_EXPLODE) { + RETURN_ERROR(AVERROR_INVALIDDATA); + } + type = 0; + } + + /* picture mimetype */ + len = avio_rb32(pb); + if (len <= 0 || + avio_read(pb, mimetype, FFMIN(len, sizeof(mimetype) - 1)) != len) { + av_log(s, AV_LOG_ERROR, "Could not read mimetype from an attached " + "picture.\n"); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR_INVALIDDATA; + goto fail; + } + mimetype[len] = 0; + + while (mime->id != AV_CODEC_ID_NONE) { + if (!strncmp(mime->str, mimetype, sizeof(mimetype))) { + id = mime->id; + break; + } + mime++; + } + if (id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_ERROR, "Unknown attached picture mimetype: %s.\n", + mimetype); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + /* picture description */ + len = avio_rb32(pb); + if (len > 0) { + if (!(desc = av_malloc(len + 1))) { + RETURN_ERROR(AVERROR(ENOMEM)); + } + + if (avio_read(pb, desc, len) != len) { + av_log(s, AV_LOG_ERROR, "Error reading attached picture description.\n"); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR(EIO); + goto fail; + } + desc[len] = 0; + } + + /* picture metadata */ + width = avio_rb32(pb); + height = avio_rb32(pb); + avio_skip(pb, 8); + + /* picture data */ + len = avio_rb32(pb); + if (len <= 0) { + av_log(s, AV_LOG_ERROR, "Invalid attached picture size: %d.\n", len); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (!(data = av_buffer_alloc(len))) { + RETURN_ERROR(AVERROR(ENOMEM)); + } + if (avio_read(pb, data->data, len) != len) { + av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n"); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR(EIO); + goto fail; + } + + st = avformat_new_stream(s, NULL); + if (!st) { + RETURN_ERROR(AVERROR(ENOMEM)); + } + + av_init_packet(&st->attached_pic); + st->attached_pic.buf = data; + st->attached_pic.data = data->data; + st->attached_pic.size = len; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = id; + st->codec->width = width; + st->codec->height = height; + av_dict_set(&st->metadata, "comment", ff_id3v2_picture_types[type], 0); + if (desc) + av_dict_set(&st->metadata, "title", desc, AV_DICT_DONT_STRDUP_VAL); + + av_freep(&pb); + + return 0; + +fail: + av_buffer_unref(&data); + av_freep(&desc); + av_freep(&pb); + return ret; + +} + +static int flac_read_header(AVFormatContext *s) +{ + int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0; + uint8_t header[4]; + uint8_t *buffer=NULL; + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_FLAC; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + /* the parameters will be extracted from the compressed bitstream */ + + /* if fLaC marker is not found, assume there is no header */ + if (avio_rl32(s->pb) != MKTAG('f','L','a','C')) { + avio_seek(s->pb, -4, SEEK_CUR); + return 0; + } + + /* process metadata blocks */ + while (!url_feof(s->pb) && !metadata_last) { + avio_read(s->pb, header, 4); + avpriv_flac_parse_block_header(header, &metadata_last, &metadata_type, + &metadata_size); + switch (metadata_type) { + /* allocate and read metadata block for supported types */ + case FLAC_METADATA_TYPE_STREAMINFO: + case FLAC_METADATA_TYPE_CUESHEET: + case FLAC_METADATA_TYPE_PICTURE: + case FLAC_METADATA_TYPE_VORBIS_COMMENT: + buffer = av_mallocz(metadata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!buffer) { + return AVERROR(ENOMEM); + } + if (avio_read(s->pb, buffer, metadata_size) != metadata_size) { + RETURN_ERROR(AVERROR(EIO)); + } + break; + /* skip metadata block for unsupported types */ + default: + ret = avio_skip(s->pb, metadata_size); + if (ret < 0) + return ret; + } + + if (metadata_type == FLAC_METADATA_TYPE_STREAMINFO) { + FLACStreaminfo si; + /* STREAMINFO can only occur once */ + if (found_streaminfo) { + RETURN_ERROR(AVERROR_INVALIDDATA); + } + if (metadata_size != FLAC_STREAMINFO_SIZE) { + RETURN_ERROR(AVERROR_INVALIDDATA); + } + found_streaminfo = 1; + st->codec->extradata = buffer; + st->codec->extradata_size = metadata_size; + buffer = NULL; + + /* get codec params from STREAMINFO header */ + avpriv_flac_parse_streaminfo(st->codec, &si, st->codec->extradata); + + /* set time base and duration */ + if (si.samplerate > 0) { + avpriv_set_pts_info(st, 64, 1, si.samplerate); + if (si.samples > 0) + st->duration = si.samples; + } + } else if (metadata_type == FLAC_METADATA_TYPE_CUESHEET) { + uint8_t isrc[13]; + uint64_t start; + const uint8_t *offset; + int i, chapters, track, ti; + if (metadata_size < 431) + RETURN_ERROR(AVERROR_INVALIDDATA); + offset = buffer + 395; + chapters = bytestream_get_byte(&offset) - 1; + if (chapters <= 0) + RETURN_ERROR(AVERROR_INVALIDDATA); + for (i = 0; i < chapters; i++) { + if (offset + 36 - buffer > metadata_size) + RETURN_ERROR(AVERROR_INVALIDDATA); + start = bytestream_get_be64(&offset); + track = bytestream_get_byte(&offset); + bytestream_get_buffer(&offset, isrc, 12); + isrc[12] = 0; + offset += 14; + ti = bytestream_get_byte(&offset); + if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA); + offset += ti * 12; + avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc); + } + av_freep(&buffer); + } else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) { + ret = parse_picture(s, buffer, metadata_size); + av_freep(&buffer); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n"); + return ret; + } + } else { + /* STREAMINFO must be the first block */ + if (!found_streaminfo) { + RETURN_ERROR(AVERROR_INVALIDDATA); + } + /* process supported blocks other than STREAMINFO */ + if (metadata_type == FLAC_METADATA_TYPE_VORBIS_COMMENT) { + if (ff_vorbis_comment(s, &s->metadata, buffer, metadata_size)) { + av_log(s, AV_LOG_WARNING, "error parsing VorbisComment metadata\n"); + } + } + av_freep(&buffer); + } + } + + return 0; + +fail: + av_free(buffer); + return ret; +} + +static int flac_probe(AVProbeData *p) +{ + if (p->buf_size < 4 || memcmp(p->buf, "fLaC", 4)) + return 0; + return AVPROBE_SCORE_MAX/2; +} + +AVInputFormat ff_flac_demuxer = { + .name = "flac", + .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), + .read_probe = flac_probe, + .read_header = flac_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "flac", + .raw_codec_id = AV_CODEC_ID_FLAC, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,25 @@ +libavformat/flacdec.o libavformat/flacdec.o: libavformat/flacdec.c \ + libavcodec/flac.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/id3v2.h \ + libavformat/internal.h libavformat/metadata.h libavformat/rawdec.h \ + libavutil/opt.h libavformat/oggdec.h libavformat/vorbiscomment.h \ + libavcodec/bytestream.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacdec.o Binary file ffmpeg/libavformat/flacdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,139 @@ +/* + * raw FLAC muxer + * Copyright (c) 2006-2009 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/flac.h" +#include "avformat.h" +#include "flacenc.h" +#include "vorbiscomment.h" +#include "libavcodec/bytestream.h" + + +static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_bytes, + int last_block) +{ + avio_w8(pb, last_block ? 0x81 : 0x01); + avio_wb24(pb, n_padding_bytes); + while (n_padding_bytes > 0) { + avio_w8(pb, 0); + n_padding_bytes--; + } + return 0; +} + +static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, + int last_block, int bitexact) +{ + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; + unsigned int len, count; + uint8_t *p, *p0; + + ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL); + + len = ff_vorbiscomment_length(*m, vendor, &count); + p0 = av_malloc(len+4); + if (!p0) + return AVERROR(ENOMEM); + p = p0; + + bytestream_put_byte(&p, last_block ? 0x84 : 0x04); + bytestream_put_be24(&p, len); + ff_vorbiscomment_write(&p, m, vendor, count); + + avio_write(pb, p0, len+4); + av_freep(&p0); + p = NULL; + + return 0; +} + +static int flac_write_header(struct AVFormatContext *s) +{ + int ret; + AVCodecContext *codec = s->streams[0]->codec; + + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + if (codec->codec_id != AV_CODEC_ID_FLAC) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + ret = ff_flac_write_header(s->pb, codec, 0); + if (ret) + return ret; + + ret = flac_write_block_comment(s->pb, &s->metadata, 0, + codec->flags & CODEC_FLAG_BITEXACT); + if (ret) + return ret; + + /* The command line flac encoder defaults to placing a seekpoint + * every 10s. So one might add padding to allow that later + * but there seems to be no simple way to get the duration here. + * So let's try the flac default of 8192 bytes */ + flac_write_block_padding(s->pb, 8192, 1); + + return ret; +} + +static int flac_write_trailer(struct AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + uint8_t *streaminfo; + enum FLACExtradataFormat format; + int64_t file_size; + + if (!avpriv_flac_is_extradata_valid(s->streams[0]->codec, &format, &streaminfo)) + return -1; + + if (pb->seekable) { + /* rewrite the STREAMINFO header block data */ + file_size = avio_tell(pb); + avio_seek(pb, 8, SEEK_SET); + avio_write(pb, streaminfo, FLAC_STREAMINFO_SIZE); + avio_seek(pb, file_size, SEEK_SET); + avio_flush(pb); + } else { + av_log(s, AV_LOG_WARNING, "unable to rewrite FLAC header.\n"); + } + return 0; +} + +static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} + +AVOutputFormat ff_flac_muxer = { + .name = "flac", + .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), + .mime_type = "audio/x-flac", + .extensions = "flac", + .audio_codec = AV_CODEC_ID_FLAC, + .video_codec = AV_CODEC_ID_NONE, + .write_header = flac_write_header, + .write_packet = flac_write_packet, + .write_trailer = flac_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +libavformat/flacenc.o libavformat/flacenc.o: libavformat/flacenc.c \ + libavcodec/flac.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/flacenc.h \ + libavcodec/bytestream.h libavformat/vorbiscomment.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacenc.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,32 @@ +/* + * raw FLAC muxer + * Copyright (C) 2009 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_FLACENC_H +#define AVFORMAT_FLACENC_H + +#include "libavcodec/flac.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" + +int ff_flac_write_header(AVIOContext *pb, AVCodecContext *codec, + int last_block); + +#endif /* AVFORMAT_FLACENC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc.o Binary file ffmpeg/libavformat/flacenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc_header.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacenc_header.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,47 @@ +/* + * raw FLAC muxer + * Copyright (C) 2009 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/flac.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "flacenc.h" + +int ff_flac_write_header(AVIOContext *pb, AVCodecContext *codec, + int last_block) +{ + uint8_t header[8] = { + 0x66, 0x4C, 0x61, 0x43, 0x00, 0x00, 0x00, 0x22 + }; + uint8_t *streaminfo; + enum FLACExtradataFormat format; + + header[4] = last_block ? 0x80 : 0x00; + if (!avpriv_flac_is_extradata_valid(codec, &format, &streaminfo)) + return -1; + + /* write "fLaC" stream marker and first metadata block header */ + avio_write(pb, header, 8); + + /* write STREAMINFO */ + avio_write(pb, streaminfo, FLAC_STREAMINFO_SIZE); + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc_header.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flacenc_header.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/flacenc_header.o libavformat/flacenc_header.o: \ + libavformat/flacenc_header.c libavcodec/flac.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/bytestream.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/flacenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flacenc_header.o Binary file ffmpeg/libavformat/flacenc_header.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flic.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,269 @@ +/* + * FLI/FLC Animation File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FLI/FLC file demuxer + * by Mike Melanson (melanson@pcisys.net) + * for more information on the .fli/.flc file format and all of its many + * variations, visit: + * http://www.compuphase.com/flic.htm + * + * This demuxer handles standard 0xAF11- and 0xAF12-type FLIs. It also handles + * special FLIs from the PC games "Magic Carpet" and "X-COM: Terror from the Deep". + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define FLIC_FILE_MAGIC_1 0xAF11 +#define FLIC_FILE_MAGIC_2 0xAF12 +#define FLIC_FILE_MAGIC_3 0xAF44 /* Flic Type for Extended FLX Format which + originated in Dave's Targa Animator (DTA) */ +#define FLIC_CHUNK_MAGIC_1 0xF1FA +#define FLIC_CHUNK_MAGIC_2 0xF5FA +#define FLIC_MC_SPEED 5 /* speed for Magic Carpet game FLIs */ +#define FLIC_DEFAULT_SPEED 5 /* for FLIs that have 0 speed */ +#define FLIC_TFTD_CHUNK_AUDIO 0xAAAA /* Audio chunk. Used in Terror from the Deep. + Has 10 B extra header not accounted for in the chunk header */ +#define FLIC_TFTD_SAMPLE_RATE 22050 + +#define FLIC_HEADER_SIZE 128 +#define FLIC_PREAMBLE_SIZE 6 + +typedef struct FlicDemuxContext { + int video_stream_index; + int audio_stream_index; + int frame_number; +} FlicDemuxContext; + +static int flic_probe(AVProbeData *p) +{ + int magic_number; + + if(p->buf_size < FLIC_HEADER_SIZE) + return 0; + + magic_number = AV_RL16(&p->buf[4]); + if ((magic_number != FLIC_FILE_MAGIC_1) && + (magic_number != FLIC_FILE_MAGIC_2) && + (magic_number != FLIC_FILE_MAGIC_3)) + return 0; + + if(AV_RL16(&p->buf[0x10]) != FLIC_CHUNK_MAGIC_1){ + if(AV_RL32(&p->buf[0x10]) > 2000) + return 0; + } + + if( AV_RL16(&p->buf[0x08]) > 4096 + || AV_RL16(&p->buf[0x0A]) > 4096) + return 0; + + + return AVPROBE_SCORE_MAX; +} + +static int flic_read_header(AVFormatContext *s) +{ + FlicDemuxContext *flic = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char header[FLIC_HEADER_SIZE]; + AVStream *st, *ast; + int speed; + int magic_number; + unsigned char preamble[FLIC_PREAMBLE_SIZE]; + + flic->frame_number = 0; + + /* load the whole header and pull out the width and height */ + if (avio_read(pb, header, FLIC_HEADER_SIZE) != FLIC_HEADER_SIZE) + return AVERROR(EIO); + + magic_number = AV_RL16(&header[4]); + speed = AV_RL32(&header[0x10]); + if (speed == 0) + speed = FLIC_DEFAULT_SPEED; + + /* initialize the decoder streams */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + flic->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_FLIC; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = AV_RL16(&header[0x08]); + st->codec->height = AV_RL16(&header[0x0A]); + + if (!st->codec->width || !st->codec->height) { + /* Ugly hack needed for the following sample: */ + /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */ + av_log(s, AV_LOG_WARNING, + "File with no specified width/height. Trying 640x480.\n"); + st->codec->width = 640; + st->codec->height = 480; + } + + /* send over the whole 128-byte FLIC header */ + st->codec->extradata_size = FLIC_HEADER_SIZE; + st->codec->extradata = av_malloc(FLIC_HEADER_SIZE); + memcpy(st->codec->extradata, header, FLIC_HEADER_SIZE); + + /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */ + if (avio_read(pb, preamble, FLIC_PREAMBLE_SIZE) != FLIC_PREAMBLE_SIZE) { + av_log(s, AV_LOG_ERROR, "Failed to peek at preamble\n"); + return AVERROR(EIO); + } + + avio_seek(pb, -FLIC_PREAMBLE_SIZE, SEEK_CUR); + + /* Time to figure out the framerate: + * If the first preamble's magic number is 0xAAAA then this file is from + * X-COM: Terror from the Deep. If on the other hand there is a FLIC chunk + * magic number at offset 0x10 assume this file is from Magic Carpet instead. + * If neither of the above is true then this is a normal FLIC file. + */ + if (AV_RL16(&preamble[4]) == FLIC_TFTD_CHUNK_AUDIO) { + /* TFTD videos have an extra 22050 Hz 8-bit mono audio stream */ + ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + + flic->audio_stream_index = ast->index; + + /* all audio frames are the same size, so use the size of the first chunk for block_align */ + ast->codec->block_align = AV_RL32(&preamble[0]); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_PCM_U8; + ast->codec->codec_tag = 0; + ast->codec->sample_rate = FLIC_TFTD_SAMPLE_RATE; + ast->codec->channels = 1; + ast->codec->bit_rate = st->codec->sample_rate * 8; + ast->codec->bits_per_coded_sample = 8; + ast->codec->channel_layout = AV_CH_LAYOUT_MONO; + ast->codec->extradata_size = 0; + + /* Since the header information is incorrect we have to figure out the + * framerate using block_align and the fact that the audio is 22050 Hz. + * We usually have two cases: 2205 -> 10 fps and 1470 -> 15 fps */ + avpriv_set_pts_info(st, 64, ast->codec->block_align, FLIC_TFTD_SAMPLE_RATE); + avpriv_set_pts_info(ast, 64, 1, FLIC_TFTD_SAMPLE_RATE); + } else if (AV_RL16(&header[0x10]) == FLIC_CHUNK_MAGIC_1) { + avpriv_set_pts_info(st, 64, FLIC_MC_SPEED, 70); + + /* rewind the stream since the first chunk is at offset 12 */ + avio_seek(pb, 12, SEEK_SET); + + /* send over abbreviated FLIC header chunk */ + av_free(st->codec->extradata); + st->codec->extradata_size = 12; + st->codec->extradata = av_malloc(12); + memcpy(st->codec->extradata, header, 12); + + } else if (magic_number == FLIC_FILE_MAGIC_1) { + avpriv_set_pts_info(st, 64, speed, 70); + } else if ((magic_number == FLIC_FILE_MAGIC_2) || + (magic_number == FLIC_FILE_MAGIC_3)) { + avpriv_set_pts_info(st, 64, speed, 1000); + } else { + av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static int flic_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + FlicDemuxContext *flic = s->priv_data; + AVIOContext *pb = s->pb; + int packet_read = 0; + unsigned int size; + int magic; + int ret = 0; + unsigned char preamble[FLIC_PREAMBLE_SIZE]; + + while (!packet_read) { + + if ((ret = avio_read(pb, preamble, FLIC_PREAMBLE_SIZE)) != + FLIC_PREAMBLE_SIZE) { + ret = AVERROR(EIO); + break; + } + + size = AV_RL32(&preamble[0]); + magic = AV_RL16(&preamble[4]); + + if (((magic == FLIC_CHUNK_MAGIC_1) || (magic == FLIC_CHUNK_MAGIC_2)) && size > FLIC_PREAMBLE_SIZE) { + if (av_new_packet(pkt, size)) { + ret = AVERROR(EIO); + break; + } + pkt->stream_index = flic->video_stream_index; + pkt->pts = flic->frame_number++; + pkt->pos = avio_tell(pb); + memcpy(pkt->data, preamble, FLIC_PREAMBLE_SIZE); + ret = avio_read(pb, pkt->data + FLIC_PREAMBLE_SIZE, + size - FLIC_PREAMBLE_SIZE); + if (ret != size - FLIC_PREAMBLE_SIZE) { + av_free_packet(pkt); + ret = AVERROR(EIO); + } + packet_read = 1; + } else if (magic == FLIC_TFTD_CHUNK_AUDIO) { + if (av_new_packet(pkt, size)) { + ret = AVERROR(EIO); + break; + } + + /* skip useless 10B sub-header (yes, it's not accounted for in the chunk header) */ + avio_skip(pb, 10); + + pkt->stream_index = flic->audio_stream_index; + pkt->pos = avio_tell(pb); + ret = avio_read(pb, pkt->data, size); + + if (ret != size) { + av_free_packet(pkt); + ret = AVERROR(EIO); + } + + packet_read = 1; + } else { + /* not interested in this chunk */ + avio_skip(pb, size - 6); + } + } + + return ret; +} + +AVInputFormat ff_flic_demuxer = { + .name = "flic", + .long_name = NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation"), + .priv_data_size = sizeof(FlicDemuxContext), + .read_probe = flic_probe, + .read_header = flic_read_header, + .read_packet = flic_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flic.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flic.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/flic.o libavformat/flic.o: libavformat/flic.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flic.o Binary file ffmpeg/libavformat/flic.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flv.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,138 @@ +/* + * FLV common header + * + * Copyright (c) 2006 The FFmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FLV common header + */ + +#ifndef AVFORMAT_FLV_H +#define AVFORMAT_FLV_H + +/* offsets for packed values */ +#define FLV_AUDIO_SAMPLESSIZE_OFFSET 1 +#define FLV_AUDIO_SAMPLERATE_OFFSET 2 +#define FLV_AUDIO_CODECID_OFFSET 4 + +#define FLV_VIDEO_FRAMETYPE_OFFSET 4 + +/* bitmasks to isolate specific values */ +#define FLV_AUDIO_CHANNEL_MASK 0x01 +#define FLV_AUDIO_SAMPLESIZE_MASK 0x02 +#define FLV_AUDIO_SAMPLERATE_MASK 0x0c +#define FLV_AUDIO_CODECID_MASK 0xf0 + +#define FLV_VIDEO_CODECID_MASK 0x0f +#define FLV_VIDEO_FRAMETYPE_MASK 0xf0 + +#define AMF_END_OF_OBJECT 0x09 + +#define KEYFRAMES_TAG "keyframes" +#define KEYFRAMES_TIMESTAMP_TAG "times" +#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" + + +enum { + FLV_HEADER_FLAG_HASVIDEO = 1, + FLV_HEADER_FLAG_HASAUDIO = 4, +}; + +enum { + FLV_TAG_TYPE_AUDIO = 0x08, + FLV_TAG_TYPE_VIDEO = 0x09, + FLV_TAG_TYPE_META = 0x12, +}; + +enum { + FLV_STREAM_TYPE_VIDEO, + FLV_STREAM_TYPE_AUDIO, + FLV_STREAM_TYPE_DATA, + FLV_STREAM_TYPE_NB, +}; + +enum { + FLV_MONO = 0, + FLV_STEREO = 1, +}; + +enum { + FLV_SAMPLESSIZE_8BIT = 0, + FLV_SAMPLESSIZE_16BIT = 1 << FLV_AUDIO_SAMPLESSIZE_OFFSET, +}; + +enum { + FLV_SAMPLERATE_SPECIAL = 0, /**< signifies 5512Hz and 8000Hz in the case of NELLYMOSER */ + FLV_SAMPLERATE_11025HZ = 1 << FLV_AUDIO_SAMPLERATE_OFFSET, + FLV_SAMPLERATE_22050HZ = 2 << FLV_AUDIO_SAMPLERATE_OFFSET, + FLV_SAMPLERATE_44100HZ = 3 << FLV_AUDIO_SAMPLERATE_OFFSET, +}; + +enum { + FLV_CODECID_PCM = 0, + FLV_CODECID_ADPCM = 1 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_PCM_LE = 3 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_NELLYMOSER_16KHZ_MONO = 4 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_NELLYMOSER_8KHZ_MONO = 5 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_NELLYMOSER = 6 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_PCM_ALAW = 7 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_PCM_MULAW = 8 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_SPEEX = 11<< FLV_AUDIO_CODECID_OFFSET, +}; + +enum { + FLV_CODECID_H263 = 2, + FLV_CODECID_SCREEN = 3, + FLV_CODECID_VP6 = 4, + FLV_CODECID_VP6A = 5, + FLV_CODECID_SCREEN2 = 6, + FLV_CODECID_H264 = 7, + FLV_CODECID_REALH263= 8, + FLV_CODECID_MPEG4 = 9, +}; + +enum { + FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) + FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) + FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< disposable inter frame (H.263 only) + FLV_FRAME_GENERATED_KEY = 4 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< generated key frame (reserved for server use only) + FLV_FRAME_VIDEO_INFO_CMD = 5 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< video info/command frame +}; + +typedef enum { + AMF_DATA_TYPE_NUMBER = 0x00, + AMF_DATA_TYPE_BOOL = 0x01, + AMF_DATA_TYPE_STRING = 0x02, + AMF_DATA_TYPE_OBJECT = 0x03, + AMF_DATA_TYPE_NULL = 0x05, + AMF_DATA_TYPE_UNDEFINED = 0x06, + AMF_DATA_TYPE_REFERENCE = 0x07, + AMF_DATA_TYPE_MIXEDARRAY = 0x08, + AMF_DATA_TYPE_OBJECT_END = 0x09, + AMF_DATA_TYPE_ARRAY = 0x0a, + AMF_DATA_TYPE_DATE = 0x0b, + AMF_DATA_TYPE_LONG_STRING = 0x0c, + AMF_DATA_TYPE_UNSUPPORTED = 0x0d, +} AMFDataType; + +#endif /* AVFORMAT_FLV_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flvdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,932 @@ +/* + * FLV demuxer + * Copyright (c) 2003 The FFmpeg Project + * + * This demuxer will generate a 1 byte extradata for VP6F content. + * It is composed of: + * - upper 4bits: difference between encoded width and visible width + * - lower 4bits: difference between encoded height and visible height + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/intfloat.h" +#include "libavutil/mathematics.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/mpeg4audio.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "flv.h" + +#define VALIDATE_INDEX_TS_THRESH 2500 + +typedef struct { + const AVClass *class; ///< Class for private options. + int trust_metadata; ///< configure streams according onMetaData + int wrong_dts; ///< wrong dts due to negative cts + uint8_t *new_extradata[FLV_STREAM_TYPE_NB]; + int new_extradata_size[FLV_STREAM_TYPE_NB]; + int last_sample_rate; + int last_channels; + struct { + int64_t dts; + int64_t pos; + } validate_index[2]; + int validate_next; + int validate_count; + int searched_for_end; +} FLVContext; + +static int flv_probe(AVProbeData *p) +{ + const uint8_t *d; + + d = p->buf; + if (d[0] == 'F' && d[1] == 'L' && d[2] == 'V' && d[3] < 5 && d[5]==0 && AV_RB32(d+5)>8) { + return AVPROBE_SCORE_MAX; + } + return 0; +} + +static AVStream *create_stream(AVFormatContext *s, int codec_type) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return NULL; + st->codec->codec_type = codec_type; + if(s->nb_streams>=3 ||( s->nb_streams==2 + && s->streams[0]->codec->codec_type != AVMEDIA_TYPE_DATA + && s->streams[1]->codec->codec_type != AVMEDIA_TYPE_DATA)) + s->ctx_flags &= ~AVFMTCTX_NOHEADER; + + avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ + return st; +} +static int flv_same_audio_codec(AVCodecContext *acodec, int flags) +{ + int bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8; + int flv_codecid = flags & FLV_AUDIO_CODECID_MASK; + int codec_id; + + if (!acodec->codec_id && !acodec->codec_tag) + return 1; + + if (acodec->bits_per_coded_sample != bits_per_coded_sample) + return 0; + + switch(flv_codecid) { + //no distinction between S16 and S8 PCM codec flags + case FLV_CODECID_PCM: + codec_id = bits_per_coded_sample == 8 ? AV_CODEC_ID_PCM_U8 : +#if HAVE_BIGENDIAN + AV_CODEC_ID_PCM_S16BE; +#else + AV_CODEC_ID_PCM_S16LE; +#endif + return codec_id == acodec->codec_id; + case FLV_CODECID_PCM_LE: + codec_id = bits_per_coded_sample == 8 ? AV_CODEC_ID_PCM_U8 : AV_CODEC_ID_PCM_S16LE; + return codec_id == acodec->codec_id; + case FLV_CODECID_AAC: + return acodec->codec_id == AV_CODEC_ID_AAC; + case FLV_CODECID_ADPCM: + return acodec->codec_id == AV_CODEC_ID_ADPCM_SWF; + case FLV_CODECID_SPEEX: + return acodec->codec_id == AV_CODEC_ID_SPEEX; + case FLV_CODECID_MP3: + return acodec->codec_id == AV_CODEC_ID_MP3; + case FLV_CODECID_NELLYMOSER_8KHZ_MONO: + case FLV_CODECID_NELLYMOSER_16KHZ_MONO: + case FLV_CODECID_NELLYMOSER: + return acodec->codec_id == AV_CODEC_ID_NELLYMOSER; + case FLV_CODECID_PCM_MULAW: + return acodec->sample_rate == 8000 && + acodec->codec_id == AV_CODEC_ID_PCM_MULAW; + case FLV_CODECID_PCM_ALAW: + return acodec->sample_rate = 8000 && + acodec->codec_id == AV_CODEC_ID_PCM_ALAW; + default: + return acodec->codec_tag == (flv_codecid >> FLV_AUDIO_CODECID_OFFSET); + } +} + +static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, AVCodecContext *acodec, int flv_codecid) { + switch(flv_codecid) { + //no distinction between S16 and S8 PCM codec flags + case FLV_CODECID_PCM: + acodec->codec_id = acodec->bits_per_coded_sample == 8 ? AV_CODEC_ID_PCM_U8 : +#if HAVE_BIGENDIAN + AV_CODEC_ID_PCM_S16BE; +#else + AV_CODEC_ID_PCM_S16LE; +#endif + break; + case FLV_CODECID_PCM_LE: + acodec->codec_id = acodec->bits_per_coded_sample == 8 ? AV_CODEC_ID_PCM_U8 : AV_CODEC_ID_PCM_S16LE; break; + case FLV_CODECID_AAC : acodec->codec_id = AV_CODEC_ID_AAC; break; + case FLV_CODECID_ADPCM: acodec->codec_id = AV_CODEC_ID_ADPCM_SWF; break; + case FLV_CODECID_SPEEX: + acodec->codec_id = AV_CODEC_ID_SPEEX; + acodec->sample_rate = 16000; + break; + case FLV_CODECID_MP3 : acodec->codec_id = AV_CODEC_ID_MP3 ; astream->need_parsing = AVSTREAM_PARSE_FULL; break; + case FLV_CODECID_NELLYMOSER_8KHZ_MONO: + acodec->sample_rate = 8000; //in case metadata does not otherwise declare samplerate + acodec->codec_id = AV_CODEC_ID_NELLYMOSER; + break; + case FLV_CODECID_NELLYMOSER_16KHZ_MONO: + acodec->sample_rate = 16000; + acodec->codec_id = AV_CODEC_ID_NELLYMOSER; + break; + case FLV_CODECID_NELLYMOSER: + acodec->codec_id = AV_CODEC_ID_NELLYMOSER; + break; + case FLV_CODECID_PCM_MULAW: + acodec->sample_rate = 8000; + acodec->codec_id = AV_CODEC_ID_PCM_MULAW; + break; + case FLV_CODECID_PCM_ALAW: + acodec->sample_rate = 8000; + acodec->codec_id = AV_CODEC_ID_PCM_ALAW; + break; + default: + av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n", flv_codecid >> FLV_AUDIO_CODECID_OFFSET); + acodec->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET; + } +} + +static int flv_same_video_codec(AVCodecContext *vcodec, int flags) +{ + int flv_codecid = flags & FLV_VIDEO_CODECID_MASK; + + if (!vcodec->codec_id && !vcodec->codec_tag) + return 1; + + switch (flv_codecid) { + case FLV_CODECID_H263: + return vcodec->codec_id == AV_CODEC_ID_FLV1; + case FLV_CODECID_SCREEN: + return vcodec->codec_id == AV_CODEC_ID_FLASHSV; + case FLV_CODECID_SCREEN2: + return vcodec->codec_id == AV_CODEC_ID_FLASHSV2; + case FLV_CODECID_VP6: + return vcodec->codec_id == AV_CODEC_ID_VP6F; + case FLV_CODECID_VP6A: + return vcodec->codec_id == AV_CODEC_ID_VP6A; + case FLV_CODECID_H264: + return vcodec->codec_id == AV_CODEC_ID_H264; + default: + return vcodec->codec_tag == flv_codecid; + } +} + +static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_codecid, int read) { + AVCodecContext *vcodec = vstream->codec; + switch(flv_codecid) { + case FLV_CODECID_H263 : vcodec->codec_id = AV_CODEC_ID_FLV1 ; break; + case FLV_CODECID_REALH263: vcodec->codec_id = AV_CODEC_ID_H263 ; break; // Really mean it this time + case FLV_CODECID_SCREEN: vcodec->codec_id = AV_CODEC_ID_FLASHSV; break; + case FLV_CODECID_SCREEN2: vcodec->codec_id = AV_CODEC_ID_FLASHSV2; break; + case FLV_CODECID_VP6 : vcodec->codec_id = AV_CODEC_ID_VP6F ; + case FLV_CODECID_VP6A : + if(flv_codecid == FLV_CODECID_VP6A) + vcodec->codec_id = AV_CODEC_ID_VP6A; + if (read) { + if (vcodec->extradata_size != 1) { + vcodec->extradata = av_malloc(1 + FF_INPUT_BUFFER_PADDING_SIZE); + if (vcodec->extradata) + vcodec->extradata_size = 1; + } + if (vcodec->extradata) + vcodec->extradata[0] = avio_r8(s->pb); + else + avio_skip(s->pb, 1); + } + return 1; // 1 byte body size adjustment for flv_read_packet() + case FLV_CODECID_H264: + vcodec->codec_id = AV_CODEC_ID_H264; + return 3; // not 4, reading packet type will consume one byte + case FLV_CODECID_MPEG4: + vcodec->codec_id = AV_CODEC_ID_MPEG4; + return 3; + default: + av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid); + vcodec->codec_tag = flv_codecid; + } + + return 0; +} + +static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) { + int length = avio_rb16(ioc); + if(length >= buffsize) { + avio_skip(ioc, length); + return -1; + } + + avio_read(ioc, buffer, length); + + buffer[length] = '\0'; + + return length; +} + +static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) { + FLVContext *flv = s->priv_data; + unsigned int timeslen = 0, fileposlen = 0, i; + char str_val[256]; + int64_t *times = NULL; + int64_t *filepositions = NULL; + int ret = AVERROR(ENOSYS); + int64_t initial_pos = avio_tell(ioc); + + if(vstream->nb_index_entries>0){ + av_log(s, AV_LOG_WARNING, "Skiping duplicate index\n"); + return 0; + } + + if (s->flags & AVFMT_FLAG_IGNIDX) + return 0; + + while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { + int64_t** current_array; + unsigned int arraylen; + + // Expect array object in context + if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY) + break; + + arraylen = avio_rb32(ioc); + if(arraylen>>28) + break; + + if (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times){ + current_array= × + timeslen= arraylen; + }else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions){ + current_array= &filepositions; + fileposlen= arraylen; + }else // unexpected metatag inside keyframes, will not use such metadata for indexing + break; + + if (!(*current_array = av_mallocz(sizeof(**current_array) * arraylen))) { + ret = AVERROR(ENOMEM); + goto finish; + } + + for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) { + if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER) + goto invalid; + current_array[0][i] = av_int2double(avio_rb64(ioc)); + } + if (times && filepositions) { + // All done, exiting at a position allowing amf_parse_object + // to finish parsing the object + ret = 0; + break; + } + } + + if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) { + for (i = 0; i < fileposlen; i++) { + av_add_index_entry(vstream, filepositions[i], times[i]*1000, + 0, 0, AVINDEX_KEYFRAME); + if (i < 2) { + flv->validate_index[i].pos = filepositions[i]; + flv->validate_index[i].dts = times[i] * 1000; + flv->validate_count = i + 1; + } + } + } else { +invalid: + av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n"); + } + +finish: + av_freep(×); + av_freep(&filepositions); + avio_seek(ioc, initial_pos, SEEK_SET); + return ret; +} + +static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vstream, const char *key, int64_t max_pos, int depth) { + AVCodecContext *acodec, *vcodec; + FLVContext *flv = s->priv_data; + AVIOContext *ioc; + AMFDataType amf_type; + char str_val[256]; + double num_val; + + num_val = 0; + ioc = s->pb; + + amf_type = avio_r8(ioc); + + switch(amf_type) { + case AMF_DATA_TYPE_NUMBER: + num_val = av_int2double(avio_rb64(ioc)); break; + case AMF_DATA_TYPE_BOOL: + num_val = avio_r8(ioc); break; + case AMF_DATA_TYPE_STRING: + if(amf_get_string(ioc, str_val, sizeof(str_val)) < 0) + return -1; + break; + case AMF_DATA_TYPE_OBJECT: + if ((vstream || astream) && ioc->seekable && key && !strcmp(KEYFRAMES_TAG, key) && depth == 1) + if (parse_keyframes_index(s, ioc, vstream ? vstream : astream, + max_pos) < 0) + av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n"); + + while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { + if (amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0) + return -1; //if we couldn't skip, bomb out. + } + if(avio_r8(ioc) != AMF_END_OF_OBJECT) + return -1; + break; + case AMF_DATA_TYPE_NULL: + case AMF_DATA_TYPE_UNDEFINED: + case AMF_DATA_TYPE_UNSUPPORTED: + break; //these take up no additional space + case AMF_DATA_TYPE_MIXEDARRAY: + avio_skip(ioc, 4); //skip 32-bit max array index + while(avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { + //this is the only case in which we would want a nested parse to not skip over the object + if(amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0) + return -1; + } + if(avio_r8(ioc) != AMF_END_OF_OBJECT) + return -1; + break; + case AMF_DATA_TYPE_ARRAY: { + unsigned int arraylen, i; + + arraylen = avio_rb32(ioc); + for(i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) { + if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0) + return -1; //if we couldn't skip, bomb out. + } + } + break; + case AMF_DATA_TYPE_DATE: + avio_skip(ioc, 8 + 2); //timestamp (double) and UTC offset (int16) + break; + default: //unsupported type, we couldn't skip + return -1; + } + + if(depth == 1 && key) { //only look for metadata values when we are not nested and key != NULL + acodec = astream ? astream->codec : NULL; + vcodec = vstream ? vstream->codec : NULL; + + if (amf_type == AMF_DATA_TYPE_NUMBER || amf_type == AMF_DATA_TYPE_BOOL) { + if (!strcmp(key, "duration")) + s->duration = num_val * AV_TIME_BASE; + else if (!strcmp(key, "videodatarate") && vcodec && 0 <= (int)(num_val * 1024.0)) + vcodec->bit_rate = num_val * 1024.0; + else if (!strcmp(key, "audiodatarate") && acodec && 0 <= (int)(num_val * 1024.0)) + acodec->bit_rate = num_val * 1024.0; + else if (!strcmp(key, "datastream")) { + AVStream *st = create_stream(s, AVMEDIA_TYPE_DATA); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_TEXT; + } else if (flv->trust_metadata) { + if (!strcmp(key, "videocodecid") && vcodec) { + flv_set_video_codec(s, vstream, num_val, 0); + } else + if (!strcmp(key, "audiocodecid") && acodec) { + int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET; + flv_set_audio_codec(s, astream, acodec, id); + } else + if (!strcmp(key, "audiosamplerate") && acodec) { + acodec->sample_rate = num_val; + } else if (!strcmp(key, "audiosamplesize") && acodec) { + acodec->bits_per_coded_sample = num_val; + } else if (!strcmp(key, "stereo") && acodec) { + acodec->channels = num_val + 1; + acodec->channel_layout = acodec->channels == 2 ? + AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; + } else + if (!strcmp(key, "width") && vcodec) { + vcodec->width = num_val; + } else + if (!strcmp(key, "height") && vcodec) { + vcodec->height = num_val; + } + } + } + + if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 && + ((!acodec && !strcmp(key, "audiocodecid")) || + (!vcodec && !strcmp(key, "videocodecid")))) + s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object + + if (!strcmp(key, "duration") || + !strcmp(key, "filesize") || + !strcmp(key, "width") || + !strcmp(key, "height") || + !strcmp(key, "videodatarate") || + !strcmp(key, "framerate") || + !strcmp(key, "videocodecid") || + !strcmp(key, "audiodatarate") || + !strcmp(key, "audiosamplerate") || + !strcmp(key, "audiosamplesize") || + !strcmp(key, "stereo") || + !strcmp(key, "audiocodecid")) + return 0; + + if(amf_type == AMF_DATA_TYPE_BOOL) { + av_strlcpy(str_val, num_val > 0 ? "true" : "false", sizeof(str_val)); + av_dict_set(&s->metadata, key, str_val, 0); + } else if(amf_type == AMF_DATA_TYPE_NUMBER) { + snprintf(str_val, sizeof(str_val), "%.f", num_val); + av_dict_set(&s->metadata, key, str_val, 0); + } else if (amf_type == AMF_DATA_TYPE_STRING) + av_dict_set(&s->metadata, key, str_val, 0); + } + + return 0; +} + +static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { + AMFDataType type; + AVStream *stream, *astream, *vstream, *dstream; + AVIOContext *ioc; + int i; + char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want. + + vstream = astream = dstream = NULL; + ioc = s->pb; + + //first object needs to be "onMetaData" string + type = avio_r8(ioc); + if (type != AMF_DATA_TYPE_STRING || + amf_get_string(ioc, buffer, sizeof(buffer)) < 0) + return -1; + + if (!strcmp(buffer, "onTextData")) + return 1; + + if (strcmp(buffer, "onMetaData")) + return -1; + + //find the streams now so that amf_parse_object doesn't need to do the lookup every time it is called. + for(i = 0; i < s->nb_streams; i++) { + stream = s->streams[i]; + if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) vstream = stream; + else if(stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) astream = stream; + else if(stream->codec->codec_type == AVMEDIA_TYPE_DATA) dstream = stream; + } + + //parse the second object (we want a mixed array) + if(amf_parse_object(s, astream, vstream, buffer, next_pos, 0) < 0) + return -1; + + return 0; +} + +static int flv_read_header(AVFormatContext *s) +{ + int offset, flags; + + avio_skip(s->pb, 4); + flags = avio_r8(s->pb); + /* old flvtool cleared this field */ + /* FIXME: better fix needed */ + if (!flags) { + flags = FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO; + av_log(s, AV_LOG_WARNING, "Broken FLV file, which says no streams present, this might fail\n"); + } + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + if(flags & FLV_HEADER_FLAG_HASVIDEO){ + if(!create_stream(s, AVMEDIA_TYPE_VIDEO)) + return AVERROR(ENOMEM); + } + if(flags & FLV_HEADER_FLAG_HASAUDIO){ + if(!create_stream(s, AVMEDIA_TYPE_AUDIO)) + return AVERROR(ENOMEM); + } + // Flag doesn't indicate whether or not there is script-data present. Must + // create that stream if it's encountered. + + offset = avio_rb32(s->pb); + avio_seek(s->pb, offset, SEEK_SET); + avio_skip(s->pb, 4); + + s->start_time = 0; + + return 0; +} + +static int flv_read_close(AVFormatContext *s) +{ + int i; + FLVContext *flv = s->priv_data; + for(i=0; inew_extradata[i]); + return 0; +} + +static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size) +{ + av_free(st->codec->extradata); + st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = size; + avio_read(s->pb, st->codec->extradata, st->codec->extradata_size); + return 0; +} + +static int flv_queue_extradata(FLVContext *flv, AVIOContext *pb, int stream, + int size) +{ + av_free(flv->new_extradata[stream]); + flv->new_extradata[stream] = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!flv->new_extradata[stream]) + return AVERROR(ENOMEM); + flv->new_extradata_size[stream] = size; + avio_read(pb, flv->new_extradata[stream], size); + return 0; +} + +static void clear_index_entries(AVFormatContext *s, int64_t pos) +{ + int i, j, out; + av_log(s, AV_LOG_WARNING, "Found invalid index entries, clearing the index.\n"); + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + /* Remove all index entries that point to >= pos */ + out = 0; + for (j = 0; j < st->nb_index_entries; j++) { + if (st->index_entries[j].pos < pos) + st->index_entries[out++] = st->index_entries[j]; + } + st->nb_index_entries = out; + } +} + + +static int flv_data_packet(AVFormatContext *s, AVPacket *pkt, + int64_t dts, int64_t next) +{ + int ret = AVERROR_INVALIDDATA, i; + AVIOContext *pb = s->pb; + AVStream *st = NULL; + AMFDataType type; + char buf[20]; + int length; + + type = avio_r8(pb); + if (type == AMF_DATA_TYPE_MIXEDARRAY) + avio_seek(pb, 4, SEEK_CUR); + else if (type != AMF_DATA_TYPE_OBJECT) + goto out; + + amf_get_string(pb, buf, sizeof(buf)); + if (strcmp(buf, "type") || avio_r8(pb) != AMF_DATA_TYPE_STRING) + goto out; + + amf_get_string(pb, buf, sizeof(buf)); + //FIXME parse it as codec_id + amf_get_string(pb, buf, sizeof(buf)); + if (strcmp(buf, "text") || avio_r8(pb) != AMF_DATA_TYPE_STRING) + goto out; + + length = avio_rb16(pb); + ret = av_get_packet(s->pb, pkt, length); + if (ret < 0) { + ret = AVERROR(EIO); + goto out; + } + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_DATA) + break; + } + + if (i == s->nb_streams) { + st = create_stream(s, AVMEDIA_TYPE_DATA); + if (!st) + goto out; + st->codec->codec_id = AV_CODEC_ID_TEXT; + } + + pkt->dts = dts; + pkt->pts = dts; + pkt->size = ret; + + pkt->stream_index = st->index; + pkt->flags |= AV_PKT_FLAG_KEY; + + avio_seek(s->pb, next + 4, SEEK_SET); +out: + return ret; +} + +static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + FLVContext *flv = s->priv_data; + int ret, i, type, size, flags; + int stream_type=-1; + int64_t next, pos; + int64_t dts, pts = AV_NOPTS_VALUE; + int av_uninit(channels); + int av_uninit(sample_rate); + AVStream *st = NULL; + + for(;;avio_skip(s->pb, 4)){ /* pkt size is repeated at end. skip it */ + pos = avio_tell(s->pb); + type = avio_r8(s->pb); + size = avio_rb24(s->pb); + dts = avio_rb24(s->pb); + dts |= avio_r8(s->pb) << 24; + av_dlog(s, "type:%d, size:%d, dts:%"PRId64"\n", type, size, dts); + if (url_feof(s->pb)) + return AVERROR_EOF; + avio_skip(s->pb, 3); /* stream id, always 0 */ + flags = 0; + + if (flv->validate_next < flv->validate_count) { + int64_t validate_pos = flv->validate_index[flv->validate_next].pos; + if (pos == validate_pos) { + if (FFABS(dts - flv->validate_index[flv->validate_next].dts) <= + VALIDATE_INDEX_TS_THRESH) { + flv->validate_next++; + } else { + clear_index_entries(s, validate_pos); + flv->validate_count = 0; + } + } else if (pos > validate_pos) { + clear_index_entries(s, validate_pos); + flv->validate_count = 0; + } + } + + if(size == 0) + continue; + + next= size + avio_tell(s->pb); + + if (type == FLV_TAG_TYPE_AUDIO) { + stream_type=FLV_STREAM_TYPE_AUDIO; + flags = avio_r8(s->pb); + size--; + } else if (type == FLV_TAG_TYPE_VIDEO) { + stream_type=FLV_STREAM_TYPE_VIDEO; + flags = avio_r8(s->pb); + size--; + if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) + goto skip; + } else if (type == FLV_TAG_TYPE_META) { + if (size > 13+1+4 && dts == 0) { // Header-type metadata stuff + flv_read_metabody(s, next); + goto skip; + } else if (dts != 0) { // Script-data "special" metadata frames - don't skip + stream_type=FLV_STREAM_TYPE_DATA; + } else { + goto skip; + } + } else { + av_log(s, AV_LOG_DEBUG, "skipping flv packet: type %d, size %d, flags %d\n", type, size, flags); + skip: + avio_seek(s->pb, next, SEEK_SET); + continue; + } + + /* skip empty data packets */ + if (!size) + continue; + + /* now find stream */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + if (stream_type == FLV_STREAM_TYPE_AUDIO) { + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + (s->audio_codec_id || flv_same_audio_codec(st->codec, flags))) { + break; + } + } else + if (stream_type == FLV_STREAM_TYPE_VIDEO) { + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + (s->video_codec_id || flv_same_video_codec(st->codec, flags))) { + break; + } + } else if (stream_type == FLV_STREAM_TYPE_DATA) { + if (st->codec->codec_type == AVMEDIA_TYPE_DATA) + break; + } + } + if(i == s->nb_streams){ + static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_DATA}; + av_log(s, AV_LOG_WARNING, "Stream discovered after head already parsed\n"); + st = create_stream(s, + stream_types[stream_type]); + if (!st) + return AVERROR(ENOMEM); + + } + av_dlog(s, "%d %X %d \n", stream_type, flags, st->discard); + if( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO))) + ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO))) + || st->discard >= AVDISCARD_ALL + ){ + avio_seek(s->pb, next, SEEK_SET); + continue; + } + if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || stream_type == FLV_STREAM_TYPE_AUDIO) + av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME); + break; + } + + // if not streamed and no duration from metadata then seek to end to find the duration from the timestamps + if(s->pb->seekable && (!s->duration || s->duration==AV_NOPTS_VALUE) && !flv->searched_for_end){ + int size; + const int64_t pos= avio_tell(s->pb); + int64_t fsize= avio_size(s->pb); +retry_duration: + avio_seek(s->pb, fsize-4, SEEK_SET); + size= avio_rb32(s->pb); + avio_seek(s->pb, fsize-3-size, SEEK_SET); + if(size == avio_rb24(s->pb) + 11){ + uint32_t ts = avio_rb24(s->pb); + ts |= avio_r8(s->pb) << 24; + if(ts) + s->duration = ts * (int64_t)AV_TIME_BASE / 1000; + else if (fsize >= 8 && fsize - 8 >= size){ + fsize -= size+4; + goto retry_duration; + } + } + + avio_seek(s->pb, pos, SEEK_SET); + flv->searched_for_end = 1; + } + + if(stream_type == FLV_STREAM_TYPE_AUDIO){ + int bits_per_coded_sample; + channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1; + sample_rate = (44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3); + bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8; + if(!st->codec->channels || !st->codec->sample_rate || !st->codec->bits_per_coded_sample) { + st->codec->channels = channels; + st->codec->channel_layout = channels == 1 ? AV_CH_LAYOUT_MONO : + AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = sample_rate; + st->codec->bits_per_coded_sample = bits_per_coded_sample; + } + if(!st->codec->codec_id){ + flv_set_audio_codec(s, st, st->codec, flags & FLV_AUDIO_CODECID_MASK); + flv->last_sample_rate = sample_rate = st->codec->sample_rate; + flv->last_channels = channels = st->codec->channels; + } else { + AVCodecContext ctx; + ctx.sample_rate = sample_rate; + flv_set_audio_codec(s, st, &ctx, flags & FLV_AUDIO_CODECID_MASK); + sample_rate = ctx.sample_rate; + } + } else if(stream_type == FLV_STREAM_TYPE_VIDEO) { + size -= flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1); + } + + if (st->codec->codec_id == AV_CODEC_ID_AAC || + st->codec->codec_id == AV_CODEC_ID_H264 || + st->codec->codec_id == AV_CODEC_ID_MPEG4) { + int type = avio_r8(s->pb); + size--; + if (st->codec->codec_id == AV_CODEC_ID_H264 || st->codec->codec_id == AV_CODEC_ID_MPEG4) { + int32_t cts = (avio_rb24(s->pb)+0xff800000)^0xff800000; // sign extension + pts = dts + cts; + if (cts < 0) { // dts are wrong + flv->wrong_dts = 1; + av_log(s, AV_LOG_WARNING, "negative cts, previous timestamps might be wrong\n"); + } + if (flv->wrong_dts) + dts = AV_NOPTS_VALUE; + } + if (type == 0 && (!st->codec->extradata || st->codec->codec_id == AV_CODEC_ID_AAC)) { + if (st->codec->extradata) { + if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0) + return ret; + ret = AVERROR(EAGAIN); + goto leave; + } + if ((ret = flv_get_extradata(s, st, size)) < 0) + return ret; + if (st->codec->codec_id == AV_CODEC_ID_AAC && 0) { + MPEG4AudioConfig cfg; + if (avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, + st->codec->extradata_size * 8, 1) >= 0) { + st->codec->channels = cfg.channels; + st->codec->channel_layout = 0; + if (cfg.ext_sample_rate) + st->codec->sample_rate = cfg.ext_sample_rate; + else + st->codec->sample_rate = cfg.sample_rate; + av_dlog(s, "mp4a config channels %d sample rate %d\n", + st->codec->channels, st->codec->sample_rate); + } + } + + ret = AVERROR(EAGAIN); + goto leave; + } + } + + /* skip empty data packets */ + if (!size) { + ret = AVERROR(EAGAIN); + goto leave; + } + + ret= av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + pkt->dts = dts; + pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts; + pkt->stream_index = st->index; + if (flv->new_extradata[stream_type]) { + uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + flv->new_extradata_size[stream_type]); + if (side) { + memcpy(side, flv->new_extradata[stream_type], + flv->new_extradata_size[stream_type]); + av_freep(&flv->new_extradata[stream_type]); + flv->new_extradata_size[stream_type] = 0; + } + } + if (stream_type == FLV_STREAM_TYPE_AUDIO && (sample_rate != flv->last_sample_rate || + channels != flv->last_channels)) { + flv->last_sample_rate = sample_rate; + flv->last_channels = channels; + ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0); + } + + if ( stream_type == FLV_STREAM_TYPE_AUDIO || + ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) || + stream_type == FLV_STREAM_TYPE_DATA) + pkt->flags |= AV_PKT_FLAG_KEY; + +leave: + avio_skip(s->pb, 4); + return ret; +} + +static int flv_read_seek(AVFormatContext *s, int stream_index, + int64_t ts, int flags) +{ + FLVContext *flv = s->priv_data; + flv->validate_count = 0; + return avio_seek_time(s->pb, stream_index, ts, flags); +} + +#define OFFSET(x) offsetof(FLVContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "flv_metadata", "Allocate streams according the onMetaData array", OFFSET(trust_metadata), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VD}, + { NULL } +}; + +static const AVClass class = { + .class_name = "flvdec", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_flv_demuxer = { + .name = "flv", + .long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), + .priv_data_size = sizeof(FLVContext), + .read_probe = flv_probe, + .read_header = flv_read_header, + .read_packet = flv_read_packet, + .read_seek = flv_read_seek, + .read_close = flv_read_close, + .extensions = "flv", + .priv_class = &class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flvdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/flvdec.o libavformat/flvdec.o: libavformat/flvdec.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/opt.h libavutil/rational.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/intfloat.h libavutil/mathematics.h libavcodec/bytestream.h \ + libavutil/common.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavcodec/mpeg4audio.h libavcodec/get_bits.h libavutil/log.h \ + libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/put_bits.h libavutil/bswap.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/flv.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvdec.o Binary file ffmpeg/libavformat/flvdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flvenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,603 @@ +/* + * FLV muxer + * Copyright (c) 2003 The FFmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "libavutil/intfloat.h" +#include "libavutil/avassert.h" +#include "avc.h" +#include "avformat.h" +#include "flv.h" +#include "internal.h" +#include "metadata.h" + + +static const AVCodecTag flv_video_codec_ids[] = { + { AV_CODEC_ID_FLV1, FLV_CODECID_H263 }, + { AV_CODEC_ID_H263, FLV_CODECID_REALH263 }, + { AV_CODEC_ID_MPEG4, FLV_CODECID_MPEG4 }, + { AV_CODEC_ID_FLASHSV, FLV_CODECID_SCREEN }, + { AV_CODEC_ID_FLASHSV2, FLV_CODECID_SCREEN2 }, + { AV_CODEC_ID_VP6F, FLV_CODECID_VP6 }, + { AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, + { AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, + { AV_CODEC_ID_H264, FLV_CODECID_H264 }, + { AV_CODEC_ID_NONE, 0 } +}; + +static const AVCodecTag flv_audio_codec_ids[] = { + { AV_CODEC_ID_MP3, FLV_CODECID_MP3 >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_PCM_U8, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_PCM_S16BE, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_PCM_S16LE, FLV_CODECID_PCM_LE >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_ADPCM_SWF, FLV_CODECID_ADPCM >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_AAC, FLV_CODECID_AAC >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_NELLYMOSER, FLV_CODECID_NELLYMOSER >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_PCM_MULAW, FLV_CODECID_PCM_MULAW >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_PCM_ALAW, FLV_CODECID_PCM_ALAW >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_SPEEX, FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET }, + { AV_CODEC_ID_NONE, 0 } +}; + +typedef struct FLVContext { + int reserved; + int64_t duration_offset; + int64_t filesize_offset; + int64_t duration; + int64_t delay; ///< first dts delay (needed for AVC & Speex) +} FLVContext; + +typedef struct FLVStreamContext { + int64_t last_ts; ///< last timestamp for each stream +} FLVStreamContext; + +static int get_audio_flags(AVFormatContext *s, AVCodecContext *enc) +{ + int flags = (enc->bits_per_coded_sample == 16) ? FLV_SAMPLESSIZE_16BIT + : FLV_SAMPLESSIZE_8BIT; + + if (enc->codec_id == AV_CODEC_ID_AAC) // specs force these parameters + return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ | + FLV_SAMPLESSIZE_16BIT | FLV_STEREO; + else if (enc->codec_id == AV_CODEC_ID_SPEEX) { + if (enc->sample_rate != 16000) { + av_log(s, AV_LOG_ERROR, + "FLV only supports wideband (16kHz) Speex audio\n"); + return AVERROR(EINVAL); + } + if (enc->channels != 1) { + av_log(s, AV_LOG_ERROR, "FLV only supports mono Speex audio\n"); + return AVERROR(EINVAL); + } + return FLV_CODECID_SPEEX | FLV_SAMPLERATE_11025HZ | FLV_SAMPLESSIZE_16BIT; + } else { + switch (enc->sample_rate) { + case 44100: + flags |= FLV_SAMPLERATE_44100HZ; + break; + case 22050: + flags |= FLV_SAMPLERATE_22050HZ; + break; + case 11025: + flags |= FLV_SAMPLERATE_11025HZ; + break; + case 16000: // nellymoser only + case 8000: // nellymoser only + case 5512: // not MP3 + if (enc->codec_id != AV_CODEC_ID_MP3) { + flags |= FLV_SAMPLERATE_SPECIAL; + break; + } + default: + av_log(s, AV_LOG_ERROR, + "FLV does not support sample rate %d, " + "choose from (44100, 22050, 11025)\n", enc->sample_rate); + return AVERROR(EINVAL); + } + } + + if (enc->channels > 1) + flags |= FLV_STEREO; + + switch (enc->codec_id) { + case AV_CODEC_ID_MP3: + flags |= FLV_CODECID_MP3 | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_PCM_U8: + flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_8BIT; + break; + case AV_CODEC_ID_PCM_S16BE: + flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_PCM_S16LE: + flags |= FLV_CODECID_PCM_LE | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_ADPCM_SWF: + flags |= FLV_CODECID_ADPCM | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_NELLYMOSER: + if (enc->sample_rate == 8000) + flags |= FLV_CODECID_NELLYMOSER_8KHZ_MONO | FLV_SAMPLESSIZE_16BIT; + else if (enc->sample_rate == 16000) + flags |= FLV_CODECID_NELLYMOSER_16KHZ_MONO | FLV_SAMPLESSIZE_16BIT; + else + flags |= FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_PCM_MULAW: + flags = FLV_CODECID_PCM_MULAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT; + break; + case AV_CODEC_ID_PCM_ALAW: + flags = FLV_CODECID_PCM_ALAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT; + break; + case 0: + flags |= enc->codec_tag << 4; + break; + default: + av_log(s, AV_LOG_ERROR, "Audio codec '%s' not compatible with FLV\n", + avcodec_get_name(enc->codec_id)); + return AVERROR(EINVAL); + } + + return flags; +} + +static void put_amf_string(AVIOContext *pb, const char *str) +{ + size_t len = strlen(str); + avio_wb16(pb, len); + avio_write(pb, str, len); +} + +static void put_avc_eos_tag(AVIOContext *pb, unsigned ts) +{ + avio_w8(pb, FLV_TAG_TYPE_VIDEO); + avio_wb24(pb, 5); /* Tag Data Size */ + avio_wb24(pb, ts); /* lower 24 bits of timestamp in ms */ + avio_w8(pb, (ts >> 24) & 0x7F); /* MSB of ts in ms */ + avio_wb24(pb, 0); /* StreamId = 0 */ + avio_w8(pb, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */ + avio_w8(pb, 2); /* AVC end of sequence */ + avio_wb24(pb, 0); /* Always 0 for AVC EOS. */ + avio_wb32(pb, 16); /* Size of FLV tag */ +} + +static void put_amf_double(AVIOContext *pb, double d) +{ + avio_w8(pb, AMF_DATA_TYPE_NUMBER); + avio_wb64(pb, av_double2int(d)); +} + +static void put_amf_bool(AVIOContext *pb, int b) +{ + avio_w8(pb, AMF_DATA_TYPE_BOOL); + avio_w8(pb, !!b); +} + +static int flv_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + FLVContext *flv = s->priv_data; + AVCodecContext *audio_enc = NULL, *video_enc = NULL, *data_enc = NULL; + int i, metadata_count = 0; + double framerate = 0.0; + int64_t metadata_size_pos, data_size, metadata_count_pos; + AVDictionaryEntry *tag = NULL; + + for (i = 0; i < s->nb_streams; i++) { + AVCodecContext *enc = s->streams[i]->codec; + FLVStreamContext *sc; + switch (enc->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (s->streams[i]->avg_frame_rate.den && + s->streams[i]->avg_frame_rate.num) { + framerate = av_q2d(s->streams[i]->avg_frame_rate); + } else { + framerate = 1 / av_q2d(s->streams[i]->codec->time_base); + } + if (video_enc) { + av_log(s, AV_LOG_ERROR, + "at most one video stream is supported in flv\n"); + return AVERROR(EINVAL); + } + video_enc = enc; + if (enc->codec_tag == 0) { + av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); + return AVERROR(EINVAL); + } + break; + case AVMEDIA_TYPE_AUDIO: + if (audio_enc) { + av_log(s, AV_LOG_ERROR, + "at most one audio stream is supported in flv\n"); + return AVERROR(EINVAL); + } + audio_enc = enc; + if (get_audio_flags(s, enc) < 0) + return AVERROR_INVALIDDATA; + break; + case AVMEDIA_TYPE_DATA: + if (enc->codec_id != AV_CODEC_ID_TEXT) { + av_log(s, AV_LOG_ERROR, "Data codec '%s' for stream %d is not compatible with FLV\n", + avcodec_get_name(enc->codec_id), i); + return AVERROR_INVALIDDATA; + } + data_enc = enc; + break; + default: + av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", + av_get_media_type_string(enc->codec_type), i); + return AVERROR(EINVAL); + } + avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ + + sc = av_mallocz(sizeof(FLVStreamContext)); + if (!sc) + return AVERROR(ENOMEM); + s->streams[i]->priv_data = sc; + sc->last_ts = -1; + } + + flv->delay = AV_NOPTS_VALUE; + + avio_write(pb, "FLV", 3); + avio_w8(pb, 1); + avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!audio_enc + + FLV_HEADER_FLAG_HASVIDEO * !!video_enc); + avio_wb32(pb, 9); + avio_wb32(pb, 0); + + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_tag == 5) { + avio_w8(pb, 8); // message type + avio_wb24(pb, 0); // include flags + avio_wb24(pb, 0); // time stamp + avio_wb32(pb, 0); // reserved + avio_wb32(pb, 11); // size + flv->reserved = 5; + } + + /* write meta_tag */ + avio_w8(pb, 18); // tag type META + metadata_size_pos = avio_tell(pb); + avio_wb24(pb, 0); // size of data part (sum of all parts below) + avio_wb24(pb, 0); // timestamp + avio_wb32(pb, 0); // reserved + + /* now data of data_size size */ + + /* first event name as a string */ + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, "onMetaData"); // 12 bytes + + /* mixed array (hash) with size and string/type/data tuples */ + avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); + metadata_count_pos = avio_tell(pb); + metadata_count = 5 * !!video_enc + + 5 * !!audio_enc + + 1 * !!data_enc + + 2; // +2 for duration and file size + + avio_wb32(pb, metadata_count); + + put_amf_string(pb, "duration"); + flv->duration_offset= avio_tell(pb); + + // fill in the guessed duration, it'll be corrected later if incorrect + put_amf_double(pb, s->duration / AV_TIME_BASE); + + if (video_enc) { + put_amf_string(pb, "width"); + put_amf_double(pb, video_enc->width); + + put_amf_string(pb, "height"); + put_amf_double(pb, video_enc->height); + + put_amf_string(pb, "videodatarate"); + put_amf_double(pb, video_enc->bit_rate / 1024.0); + + put_amf_string(pb, "framerate"); + put_amf_double(pb, framerate); + + put_amf_string(pb, "videocodecid"); + put_amf_double(pb, video_enc->codec_tag); + } + + if (audio_enc) { + put_amf_string(pb, "audiodatarate"); + put_amf_double(pb, audio_enc->bit_rate / 1024.0); + + put_amf_string(pb, "audiosamplerate"); + put_amf_double(pb, audio_enc->sample_rate); + + put_amf_string(pb, "audiosamplesize"); + put_amf_double(pb, audio_enc->codec_id == AV_CODEC_ID_PCM_U8 ? 8 : 16); + + put_amf_string(pb, "stereo"); + put_amf_bool(pb, audio_enc->channels == 2); + + put_amf_string(pb, "audiocodecid"); + put_amf_double(pb, audio_enc->codec_tag); + } + + if (data_enc) { + put_amf_string(pb, "datastream"); + put_amf_double(pb, 0.0); + } + + while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if( !strcmp(tag->key, "width") + ||!strcmp(tag->key, "height") + ||!strcmp(tag->key, "videodatarate") + ||!strcmp(tag->key, "framerate") + ||!strcmp(tag->key, "videocodecid") + ||!strcmp(tag->key, "audiodatarate") + ||!strcmp(tag->key, "audiosamplerate") + ||!strcmp(tag->key, "audiosamplesize") + ||!strcmp(tag->key, "stereo") + ||!strcmp(tag->key, "audiocodecid") + ||!strcmp(tag->key, "duration") + ||!strcmp(tag->key, "onMetaData") + ){ + av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key); + continue; + } + put_amf_string(pb, tag->key); + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, tag->value); + metadata_count++; + } + + put_amf_string(pb, "filesize"); + flv->filesize_offset = avio_tell(pb); + put_amf_double(pb, 0); // delayed write + + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + + /* write total size of tag */ + data_size = avio_tell(pb) - metadata_size_pos - 10; + + avio_seek(pb, metadata_count_pos, SEEK_SET); + avio_wb32(pb, metadata_count); + + avio_seek(pb, metadata_size_pos, SEEK_SET); + avio_wb24(pb, data_size); + avio_skip(pb, data_size + 10 - 3); + avio_wb32(pb, data_size + 11); + + for (i = 0; i < s->nb_streams; i++) { + AVCodecContext *enc = s->streams[i]->codec; + if (enc->codec_id == AV_CODEC_ID_AAC || enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { + int64_t pos; + avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? + FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); + avio_wb24(pb, 0); // size patched later + avio_wb24(pb, 0); // ts + avio_w8(pb, 0); // ts ext + avio_wb24(pb, 0); // streamid + pos = avio_tell(pb); + if (enc->codec_id == AV_CODEC_ID_AAC) { + avio_w8(pb, get_audio_flags(s, enc)); + avio_w8(pb, 0); // AAC sequence header + avio_write(pb, enc->extradata, enc->extradata_size); + } else { + avio_w8(pb, enc->codec_tag | FLV_FRAME_KEY); // flags + avio_w8(pb, 0); // AVC sequence header + avio_wb24(pb, 0); // composition time + ff_isom_write_avcc(pb, enc->extradata, enc->extradata_size); + } + data_size = avio_tell(pb) - pos; + avio_seek(pb, -data_size - 10, SEEK_CUR); + avio_wb24(pb, data_size); + avio_skip(pb, data_size + 10 - 3); + avio_wb32(pb, data_size + 11); // previous tag size + } + } + + return 0; +} + +static int flv_write_trailer(AVFormatContext *s) +{ + int64_t file_size; + + AVIOContext *pb = s->pb; + FLVContext *flv = s->priv_data; + int i; + + /* Add EOS tag */ + for (i = 0; i < s->nb_streams; i++) { + AVCodecContext *enc = s->streams[i]->codec; + FLVStreamContext *sc = s->streams[i]->priv_data; + if (enc->codec_type == AVMEDIA_TYPE_VIDEO && + (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4)) + put_avc_eos_tag(pb, sc->last_ts); + } + + file_size = avio_tell(pb); + + /* update information */ + if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) + av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n"); + else + put_amf_double(pb, flv->duration / (double)1000); + if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0) + av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n"); + else + put_amf_double(pb, file_size); + + avio_seek(pb, file_size, SEEK_SET); + return 0; +} + +static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc = s->streams[pkt->stream_index]->codec; + FLVContext *flv = s->priv_data; + FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; + unsigned ts; + int size = pkt->size; + uint8_t *data = NULL; + int flags = -1, flags_size, ret; + + if (enc->codec_id == AV_CODEC_ID_VP6 || enc->codec_id == AV_CODEC_ID_VP6F || + enc->codec_id == AV_CODEC_ID_VP6A || enc->codec_id == AV_CODEC_ID_AAC) + flags_size = 2; + else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) + flags_size = 5; + else + flags_size = 1; + + switch (enc->codec_type) { + case AVMEDIA_TYPE_VIDEO: + avio_w8(pb, FLV_TAG_TYPE_VIDEO); + + flags = enc->codec_tag; + if (flags == 0) { + av_log(s, AV_LOG_ERROR, + "Video codec '%s' is not compatible with FLV\n", + avcodec_get_name(enc->codec_id)); + return AVERROR(EINVAL); + } + + flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; + break; + case AVMEDIA_TYPE_AUDIO: + flags = get_audio_flags(s, enc); + + av_assert0(size); + + avio_w8(pb, FLV_TAG_TYPE_AUDIO); + break; + case AVMEDIA_TYPE_DATA: + avio_w8(pb, FLV_TAG_TYPE_META); + break; + default: + return AVERROR(EINVAL); + } + + if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { + /* check if extradata looks like mp4 formated */ + if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) + if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) + return ret; + } else if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && + (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + if (!s->streams[pkt->stream_index]->nb_frames) { + av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " + "use audio bitstream filter 'aac_adtstoasc' to fix it " + "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); + return AVERROR_INVALIDDATA; + } + av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); + } + + if (flv->delay == AV_NOPTS_VALUE) + flv->delay = -pkt->dts; + + if (pkt->dts < -flv->delay) { + av_log(s, AV_LOG_WARNING, + "Packets are not in the proper order with respect to DTS\n"); + return AVERROR(EINVAL); + } + + ts = pkt->dts + flv->delay; // add delay to force positive dts + + /* check Speex packet duration */ + if (enc->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) + av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " + "8 frames per packet. Adobe Flash " + "Player cannot handle this!\n"); + + if (sc->last_ts < ts) + sc->last_ts = ts; + + avio_wb24(pb, size + flags_size); + avio_wb24(pb, ts); + avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ + avio_wb24(pb, flv->reserved); + + if (enc->codec_type == AVMEDIA_TYPE_DATA) { + int data_size; + int metadata_size_pos = avio_tell(pb); + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, "onTextData"); + avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); + avio_wb32(pb, 2); + put_amf_string(pb, "type"); + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, "Text"); + put_amf_string(pb, "text"); + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, pkt->data); + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + /* write total size of tag */ + data_size = avio_tell(pb) - metadata_size_pos; + avio_seek(pb, metadata_size_pos - 10, SEEK_SET); + avio_wb24(pb, data_size); + avio_seek(pb, data_size + 10 - 3, SEEK_CUR); + avio_wb32(pb, data_size + 11); + } else { + av_assert1(flags>=0); + avio_w8(pb,flags); + if (enc->codec_id == AV_CODEC_ID_VP6) + avio_w8(pb,0); + if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) + avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); + else if (enc->codec_id == AV_CODEC_ID_AAC) + avio_w8(pb,1); // AAC raw + else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { + avio_w8(pb,1); // AVC NALU + avio_wb24(pb,pkt->pts - pkt->dts); + } + + avio_write(pb, data ? data : pkt->data, size); + + avio_wb32(pb, size + flags_size + 11); // previous tag size + flv->duration = FFMAX(flv->duration, + pkt->pts + flv->delay + pkt->duration); + } + + av_free(data); + + return pb->error; +} + +AVOutputFormat ff_flv_muxer = { + .name = "flv", + .long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), + .mime_type = "video/x-flv", + .extensions = "flv", + .priv_data_size = sizeof(FLVContext), + .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF, + .video_codec = AV_CODEC_ID_FLV1, + .write_header = flv_write_header, + .write_packet = flv_write_packet, + .write_trailer = flv_write_trailer, + .codec_tag = (const AVCodecTag* const []) { + flv_video_codec_ids, flv_audio_codec_ids, 0 + }, + .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + AVFMT_TS_NONSTRICT, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/flvenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/flvenc.o libavformat/flvenc.o: libavformat/flvenc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavutil/dict.h \ + libavutil/intfloat.h libavutil/avassert.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavformat/avc.h \ + libavformat/avio.h libavutil/common.h libavutil/log.h \ + libavformat/version.h libavutil/avutil.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavformat/flv.h \ + libavformat/internal.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/flvenc.o Binary file ffmpeg/libavformat/flvenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framecrcenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/framecrcenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,60 @@ +/* + * frame CRC encoder (for codec/format testing) + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/adler32.h" +#include "libavutil/avstring.h" +#include "avformat.h" +#include "internal.h" + +static int framecrc_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + uint32_t crc = av_adler32_update(0, pkt->data, pkt->size); + char buf[256]; + + snprintf(buf, sizeof(buf), "%d, %10"PRId64", %10"PRId64", %8d, %8d, 0x%08x", + pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size, crc); + if (pkt->flags != AV_PKT_FLAG_KEY) + av_strlcatf(buf, sizeof(buf), ", F=0x%0X", pkt->flags); + if (pkt->side_data_elems) { + int i; + av_strlcatf(buf, sizeof(buf), ", S=%d", pkt->side_data_elems); + + for (i=0; iside_data_elems; i++) { + uint32_t side_data_crc = av_adler32_update(0, + pkt->side_data[i].data, + pkt->side_data[i].size); + av_strlcatf(buf, sizeof(buf), ", %8d, 0x%08x", pkt->side_data[i].size, side_data_crc); + } + } + av_strlcatf(buf, sizeof(buf), "\n"); + avio_write(s->pb, buf, strlen(buf)); + return 0; +} + +AVOutputFormat ff_framecrc_muxer = { + .name = "framecrc", + .long_name = NULL_IF_CONFIG_SMALL("framecrc testing"), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = ff_framehash_write_header, + .write_packet = framecrc_write_packet, + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framecrcenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/framecrcenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/framecrcenc.o libavformat/framecrcenc.o: \ + libavformat/framecrcenc.c libavutil/adler32.h libavutil/attributes.h \ + libavutil/avstring.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framecrcenc.o Binary file ffmpeg/libavformat/framecrcenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framehash.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/framehash.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,33 @@ +/* + * Common functions for the frame{crc,md5} muxers + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "internal.h" + +int ff_framehash_write_header(AVFormatContext *s) +{ + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + avpriv_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); + avio_printf(s->pb, "#tb %d: %d/%d\n", i, st->time_base.num, st->time_base.den); + avio_flush(s->pb); + } + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framehash.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/framehash.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/framehash.o libavformat/framehash.o: libavformat/framehash.c \ + libavformat/internal.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/framehash.o Binary file ffmpeg/libavformat/framehash.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/frmdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/frmdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,110 @@ +/* + * Megalux Frame demuxer + * Copyright (c) 2010 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Megalux Frame demuxer + */ + +#include "libavcodec/raw.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +static const PixelFormatTag frm_pix_fmt_tags[] = { + { AV_PIX_FMT_RGB555, 1 }, + { AV_PIX_FMT_RGB0, 2 }, + { AV_PIX_FMT_RGB24, 3 }, + { AV_PIX_FMT_BGR0, 4 }, + { AV_PIX_FMT_BGRA, 5 }, + { AV_PIX_FMT_NONE, 0 }, +}; + +typedef struct { + int count; +} FrmContext; + +static int frm_read_probe(AVProbeData *p) +{ + if (p->buf_size > 8 && + p->buf[0] == 'F' && p->buf[1] == 'R' && p->buf[2] == 'M' && + AV_RL16(&p->buf[4]) && AV_RL16(&p->buf[6])) + return AVPROBE_SCORE_MAX / 4; + return 0; +} + +static int frm_read_header(AVFormatContext *avctx) +{ + AVIOContext *pb = avctx->pb; + AVStream *st = avformat_new_stream(avctx, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + avio_skip(pb, 3); + + st->codec->pix_fmt = avpriv_find_pix_fmt(frm_pix_fmt_tags, avio_r8(pb)); + if (!st->codec->pix_fmt) + return AVERROR_INVALIDDATA; + + st->codec->codec_tag = 0; + st->codec->width = avio_rl16(pb); + st->codec->height = avio_rl16(pb); + return 0; +} + +static int frm_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + FrmContext *s = avctx->priv_data; + AVCodecContext *stc = avctx->streams[0]->codec; + int packet_size, ret; + + if (s->count) + return AVERROR_EOF; + + packet_size = avpicture_get_size(stc->pix_fmt, stc->width, stc->height); + if (packet_size < 0) + return AVERROR_INVALIDDATA; + + ret = av_get_packet(avctx->pb, pkt, packet_size); + if (ret < 0) + return ret; + + if (stc->pix_fmt == AV_PIX_FMT_BGRA) { + int i; + for (i = 3; i + 1 <= pkt->size; i += 4) + pkt->data[i] = 0xFF - pkt->data[i]; + } + + pkt->stream_index = 0; + s->count++; + + return 0; +} + +AVInputFormat ff_frm_demuxer = { + .name = "frm", + .priv_data_size = sizeof(FrmContext), + .long_name = NULL_IF_CONFIG_SMALL("Megalux Frame"), + .read_probe = frm_read_probe, + .read_header = frm_read_header, + .read_packet = frm_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/frmdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/frmdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/frmdec.o libavformat/frmdec.o: libavformat/frmdec.c \ + libavcodec/raw.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/internal.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/frmdec.o Binary file ffmpeg/libavformat/frmdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g722.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g722.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,57 @@ +/* + * g722 raw demuxer + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" + +static int g722_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_G722; + st->codec->sample_rate = 16000; + st->codec->channels = 1; + + st->codec->bits_per_coded_sample = + av_get_bits_per_sample(st->codec->codec_id); + + av_assert0(st->codec->bits_per_coded_sample > 0); + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + return 0; +} + +AVInputFormat ff_g722_demuxer = { + .name = "g722", + .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), + .read_header = g722_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "g722,722", + .raw_codec_id = AV_CODEC_ID_ADPCM_G722, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g722.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g722.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/g722.o libavformat/g722.o: libavformat/g722.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/rawdec.h libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g722.o Binary file ffmpeg/libavformat/g722.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g723_1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g723_1.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,85 @@ +/* + * G.723.1 demuxer + * Copyright (c) 2010 Mohamed Naufal Basheer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * G.723.1 demuxer + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +static const uint8_t frame_size[4] = { 24, 20, 4, 1 }; + +static int g723_1_init(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_G723_1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->channels = 1; + st->codec->sample_rate = 8000; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + st->start_time = 0; + + return 0; +} + +static int g723_1_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size, byte, ret; + + pkt->pos = avio_tell(s->pb); + byte = avio_r8(s->pb); + size = frame_size[byte & 3]; + + ret = av_new_packet(pkt, size); + if (ret < 0) + return ret; + + pkt->data[0] = byte; + pkt->duration = 240; + pkt->stream_index = 0; + + ret = avio_read(s->pb, pkt->data + 1, size - 1); + if (ret < size - 1) { + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR_EOF; + } + + return pkt->size; +} + +AVInputFormat ff_g723_1_demuxer = { + .name = "g723_1", + .long_name = NULL_IF_CONFIG_SMALL("G.723.1"), + .read_header = g723_1_init, + .read_packet = g723_1_read_packet, + .extensions = "tco,rco,g723_1", + .flags = AVFMT_GENERIC_INDEX +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g723_1.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g723_1.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/g723_1.o libavformat/g723_1.o: libavformat/g723_1.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g723_1.o Binary file ffmpeg/libavformat/g723_1.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g729dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g729dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,103 @@ +/* + * G.729 raw format demuxer + * Copyright (c) 2011 Vladimir Voroshilov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +typedef struct G729DemuxerContext { + AVClass *class; + int bit_rate; +} G729DemuxerContext; + +static int g729_read_header(AVFormatContext *s) +{ + AVStream* st; + G729DemuxerContext *s1 = s->priv_data; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_G729; + st->codec->sample_rate = 8000; + st->codec->channels = 1; + + if (s1 && s1->bit_rate) { + s->bit_rate = s1->bit_rate; + } + + if (s->bit_rate == 0) { + av_log(s, AV_LOG_DEBUG, "No bitrate specified. Assuming 8000 b/s\n"); + s->bit_rate = 8000; + } + + if (s->bit_rate == 6400) { + st->codec->block_align = 8; + } else if (s->bit_rate == 8000) { + st->codec->block_align = 10; + } else { + av_log(s, AV_LOG_ERROR, "Only 8000 b/s and 6400 b/s bitrates are supported. Provided: %d b/s\n", s->bit_rate); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(st, st->codec->block_align << 3, 1, st->codec->sample_rate); + return 0; +} +static int g729_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + + ret = av_get_packet(s->pb, pkt, s->streams[0]->codec->block_align); + + pkt->stream_index = 0; + if (ret < 0) + return ret; + + pkt->dts = pkt->pts = pkt->pos / s->streams[0]->codec->block_align; + + return ret; +} + +static const AVOption g729_options[] = { + { "bit_rate", "", offsetof(G729DemuxerContext, bit_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass g729_demuxer_class = { + .class_name = "g729 demuxer", + .item_name = av_default_item_name, + .option = g729_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_g729_demuxer = { + .name = "g729", + .long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"), + .priv_data_size = sizeof(G729DemuxerContext), + .read_header = g729_read_header, + .read_packet = g729_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "g729", + .priv_class = &g729_demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g729dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/g729dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/g729dec.o libavformat/g729dec.o: libavformat/g729dec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/g729dec.o Binary file ffmpeg/libavformat/g729dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gif.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gif.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,219 @@ +/* + * Animated GIF muxer + * Copyright (c) 2000 Fabrice Bellard + * + * first version by Francois Revol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +static int gif_image_write_header(AVIOContext *pb, int width, int height, + int loop_count, uint32_t *palette) +{ + int i; + + avio_write(pb, "GIF", 3); + avio_write(pb, "89a", 3); + avio_wl16(pb, width); + avio_wl16(pb, height); + + if (palette) { + avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ + avio_w8(pb, 0x1f); /* background color index */ + avio_w8(pb, 0); /* aspect ratio */ + for (i = 0; i < 256; i++) { + const uint32_t v = palette[i] & 0xffffff; + avio_wb24(pb, v); + } + } else { + avio_w8(pb, 0); /* flags */ + avio_w8(pb, 0); /* background color index */ + avio_w8(pb, 0); /* aspect ratio */ + } + + /* "NETSCAPE EXTENSION" for looped animation GIF */ + avio_w8(pb, 0x21); /* GIF Extension code */ + avio_w8(pb, 0xff); /* Application Extension Label */ + avio_w8(pb, 0x0b); /* Length of Application Block */ + avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); + avio_w8(pb, 0x03); /* Length of Data Sub-Block */ + avio_w8(pb, 0x01); + avio_wl16(pb, (uint16_t)loop_count); + avio_w8(pb, 0x00); /* Data Sub-block Terminator */ + + return 0; +} + +typedef struct { + AVClass *class; /** Class for private options. */ + int loop; + int last_delay; + AVPacket *prev_pkt; + int duration; +} GIFContext; + +static int gif_write_header(AVFormatContext *s) +{ + GIFContext *gif = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *video_enc; + int width, height; + uint32_t palette[AVPALETTE_COUNT]; + + if (s->nb_streams != 1 || + s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO || + s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) { + av_log(s, AV_LOG_ERROR, + "GIF muxer supports only a single video GIF stream.\n"); + return AVERROR(EINVAL); + } + + video_enc = s->streams[0]->codec; + width = video_enc->width; + height = video_enc->height; + + avpriv_set_pts_info(s->streams[0], 64, 1, 100); + if (avpriv_set_systematic_pal2(palette, video_enc->pix_fmt) < 0) { + av_assert0(video_enc->pix_fmt == AV_PIX_FMT_PAL8); + gif_image_write_header(pb, width, height, gif->loop, NULL); + } else { + gif_image_write_header(pb, width, height, gif->loop, palette); + } + + avio_flush(s->pb); + return 0; +} + +static int flush_packet(AVFormatContext *s, AVPacket *new) +{ + GIFContext *gif = s->priv_data; + int size; + AVIOContext *pb = s->pb; + uint8_t flags = 0x4, transparent_color_index = 0x1f; + const uint32_t *palette; + AVPacket *pkt = gif->prev_pkt; + + if (!pkt) + return 0; + + /* Mark one colour as transparent if the input palette contains at least + * one colour that is more than 50% transparent. */ + palette = (uint32_t*)av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size); + if (palette && size != AVPALETTE_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n"); + return AVERROR_INVALIDDATA; + } + if (palette) { + unsigned i, smallest_alpha = 0xff; + + for (i = 0; i < AVPALETTE_COUNT; i++) { + const uint32_t v = palette[i]; + if (v >> 24 < smallest_alpha) { + smallest_alpha = v >> 24; + transparent_color_index = i; + } + } + if (smallest_alpha < 128) + flags |= 0x1; /* Transparent Color Flag */ + } + + if (new && new->pts != AV_NOPTS_VALUE) + gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts); + else if (!new && gif->last_delay >= 0) + gif->duration = gif->last_delay; + + /* graphic control extension block */ + avio_w8(pb, 0x21); + avio_w8(pb, 0xf9); + avio_w8(pb, 0x04); /* block size */ + avio_w8(pb, flags); + avio_wl16(pb, gif->duration); + avio_w8(pb, transparent_color_index); + avio_w8(pb, 0x00); + + avio_write(pb, pkt->data, pkt->size); + + av_free_packet(gif->prev_pkt); + if (new) + av_copy_packet(gif->prev_pkt, new); + + return 0; +} + +static int gif_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + GIFContext *gif = s->priv_data; + + if (!gif->prev_pkt) { + gif->prev_pkt = av_malloc(sizeof(*gif->prev_pkt)); + if (!gif->prev_pkt) + return AVERROR(ENOMEM); + return av_copy_packet(gif->prev_pkt, pkt); + } + return flush_packet(s, pkt); +} + +static int gif_write_trailer(AVFormatContext *s) +{ + GIFContext *gif = s->priv_data; + AVIOContext *pb = s->pb; + + flush_packet(s, NULL); + av_freep(&gif->prev_pkt); + avio_w8(pb, 0x3b); + + return 0; +} + +#define OFFSET(x) offsetof(GIFContext, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "loop", "Number of times to loop the output.", OFFSET(loop), + AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 65535, ENC }, + { "final_delay", "Force delay (in ms) after the last frame", OFFSET(last_delay), + AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC }, + { NULL }, +}; + +static const AVClass gif_muxer_class = { + .class_name = "GIF muxer", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .option = options, +}; + +AVOutputFormat ff_gif_muxer = { + .name = "gif", + .long_name = NULL_IF_CONFIG_SMALL("GIF Animation"), + .mime_type = "image/gif", + .extensions = "gif", + .priv_data_size = sizeof(GIFContext), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_GIF, + .write_header = gif_write_header, + .write_packet = gif_write_packet, + .write_trailer = gif_write_trailer, + .priv_class = &gif_muxer_class, + .flags = AVFMT_VARIABLE_FPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gif.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gif.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/gif.o libavformat/gif.o: libavformat/gif.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavutil/avassert.h libavutil/imgutils.h libavutil/pixdesc.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gif.o Binary file ffmpeg/libavformat/gif.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gifdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gifdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,324 @@ +/* + * GIF demuxer + * Copyright (c) 2012 Vitaliy E Sugrobov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * GIF demuxer. + */ + +#include "avformat.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "libavcodec/gif.h" + +typedef struct GIFDemuxContext { + const AVClass *class; + /** + * Time span in hundredths of second before + * the next frame should be drawn on screen. + */ + int delay; + /** + * Minimum allowed delay between frames in hundredths of + * second. Values below this threshold considered to be + * invalid and set to value of default_delay. + */ + int min_delay; + int default_delay; + + /** + * loop options + */ + int total_iter; + int iter_count; + int ignore_loop; +} GIFDemuxContext; + +/** + * Major web browsers display gifs at ~10-15fps when rate + * is not explicitly set or have too low values. We assume default rate to be 10. + * Default delay = 100hundredths of second / 10fps = 10hos per frame. + */ +#define GIF_DEFAULT_DELAY 10 +/** + * By default delay values less than this threshold considered to be invalid. + */ +#define GIF_MIN_DELAY 2 + +static int gif_probe(AVProbeData *p) +{ + /* check magick */ + if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6)) + return 0; + + /* width or height contains zero? */ + if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8])) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int resync(AVIOContext *pb) +{ + int i; + for (i = 0; i < 6; i++) { + int b = avio_r8(pb); + if (b != gif87a_sig[i] && b != gif89a_sig[i]) + i = -(b != 'G'); + if (url_feof(pb)) + return AVERROR_EOF; + } + return 0; +} + +static int gif_read_header(AVFormatContext *s) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int width, height, ret; + + if ((ret = resync(pb)) < 0) + return ret; + + gdc->delay = gdc->default_delay; + width = avio_rl16(pb); + height = avio_rl16(pb); + + if (width == 0 || height == 0) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + /* GIF format operates with time in "hundredths of second", + * therefore timebase is 1/100 */ + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_GIF; + st->codec->width = width; + st->codec->height = height; + + /* jump to start because gif decoder needs header data too */ + if (avio_seek(pb, 0, SEEK_SET) != 0) + return AVERROR(EIO); + + return 0; +} + +static int gif_skip_subblocks(AVIOContext *pb) +{ + int sb_size, ret = 0; + + while (0x00 != (sb_size = avio_r8(pb))) { + if ((ret = avio_skip(pb, sb_size)) < 0) + return ret; + } + + return ret; +} + +static int gif_read_ext(AVFormatContext *s) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + int sb_size, ext_label = avio_r8(pb); + int ret; + + if (ext_label == GIF_GCE_EXT_LABEL) { + if ((sb_size = avio_r8(pb)) < 4) { + av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n"); + return AVERROR_INVALIDDATA; + } + + /* skip packed fields */ + if ((ret = avio_skip(pb, 1)) < 0) + return ret; + + gdc->delay = avio_rl16(pb); + + if (gdc->delay < gdc->min_delay) + gdc->delay = gdc->default_delay; + + /* skip the rest of the Graphic Control Extension block */ + if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) + return ret; + } else if (ext_label == GIF_APP_EXT_LABEL) { + uint8_t netscape_ext[sizeof(NETSCAPE_EXT_STR)-1 + 2]; + + if ((sb_size = avio_r8(pb)) != strlen(NETSCAPE_EXT_STR)) + return 0; + ret = avio_read(pb, netscape_ext, sizeof(netscape_ext)); + if (ret < sizeof(netscape_ext)) + return ret; + gdc->total_iter = avio_rl16(pb); + if (gdc->total_iter == 0) + gdc->total_iter = -1; + } + + if ((ret = gif_skip_subblocks(pb)) < 0) + return ret; + + return 0; +} + +static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + GIFDemuxContext *gdc = s->priv_data; + AVIOContext *pb = s->pb; + int packed_fields, block_label, ct_size, + keyframe, frame_parsed = 0, ret; + int64_t frame_start = avio_tell(pb), frame_end; + unsigned char buf[6]; + + if ((ret = avio_read(pb, buf, 6)) == 6) { + keyframe = memcmp(buf, gif87a_sig, 6) == 0 || + memcmp(buf, gif89a_sig, 6) == 0; + } else if (ret < 0) { + return ret; + } else { + keyframe = 0; + } + + if (keyframe) { +parse_keyframe: + /* skip 2 bytes of width and 2 of height */ + if ((ret = avio_skip(pb, 4)) < 0) + return ret; + + packed_fields = avio_r8(pb); + + /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */ + if ((ret = avio_skip(pb, 2)) < 0) + return ret; + + /* global color table presence */ + if (packed_fields & 0x80) { + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); + + if ((ret = avio_skip(pb, ct_size)) < 0) + return ret; + } + } else { + avio_seek(pb, -ret, SEEK_CUR); + ret = AVERROR_EOF; + } + + while (GIF_TRAILER != (block_label = avio_r8(pb)) && !url_feof(pb)) { + if (block_label == GIF_EXTENSION_INTRODUCER) { + if ((ret = gif_read_ext (s)) < 0 ) + goto resync; + } else if (block_label == GIF_IMAGE_SEPARATOR) { + /* skip to last byte of Image Descriptor header */ + if ((ret = avio_skip(pb, 8)) < 0) + return ret; + + packed_fields = avio_r8(pb); + + /* local color table presence */ + if (packed_fields & 0x80) { + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); + + if ((ret = avio_skip(pb, ct_size)) < 0) + return ret; + } + + /* read LZW Minimum Code Size */ + if (avio_r8(pb) < 1) { + av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n"); + goto resync; + } + + if ((ret = gif_skip_subblocks(pb)) < 0) + goto resync; + + frame_end = avio_tell(pb); + + if (avio_seek(pb, frame_start, SEEK_SET) != frame_start) + return AVERROR(EIO); + + ret = av_get_packet(pb, pkt, frame_end - frame_start); + if (ret < 0) + return ret; + + if (keyframe) + pkt->flags |= AV_PKT_FLAG_KEY; + + pkt->stream_index = 0; + pkt->duration = gdc->delay; + + /* Graphic Control Extension's scope is single frame. + * Remove its influence. */ + gdc->delay = gdc->default_delay; + frame_parsed = 1; + + break; + } else { + av_log(s, AV_LOG_ERROR, "invalid block label\n"); +resync: + if (!keyframe) + avio_seek(pb, frame_start, SEEK_SET); + if ((ret = resync(pb)) < 0) + return ret; + frame_start = avio_tell(pb) - 6; + keyframe = 1; + goto parse_keyframe; + } + } + + if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) { + /* This might happen when there is no image block + * between extension blocks and GIF_TRAILER or EOF */ + if (!gdc->ignore_loop && (block_label == GIF_TRAILER || url_feof(pb)) + && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter)) + return avio_seek(pb, 0, SEEK_SET); + return AVERROR_EOF; + } else + return ret; +} + +static const AVOption options[] = { + { "min_delay" , "minimum valid delay between frames (in hundredths of second)", offsetof(GIFDemuxContext, min_delay) , AV_OPT_TYPE_INT, {.i64 = GIF_MIN_DELAY} , 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "default_delay", "default delay between frames (in hundredths of second)" , offsetof(GIFDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = GIF_DEFAULT_DELAY}, 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM }, + { "ignore_loop" , "ignore loop setting (netscape extension)" , offsetof(GIFDemuxContext, ignore_loop) , AV_OPT_TYPE_INT, {.i64 = 1} , 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass demuxer_class = { + .class_name = "GIF demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +AVInputFormat ff_gif_demuxer = { + .name = "gif", + .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), + .priv_data_size = sizeof(GIFDemuxContext), + .read_probe = gif_probe, + .read_header = gif_read_header, + .read_packet = gif_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gifdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gifdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/gifdec.o libavformat/gifdec.o: libavformat/gifdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/opt.h libavformat/internal.h libavcodec/gif.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gifdec.o Binary file ffmpeg/libavformat/gifdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gopher.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gopher.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,125 @@ +/* + * Gopher protocol + * + * Copyright (c) 2009 Toshimitsu Kimura + * + * based on libavformat/http.c, Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "avformat.h" +#include "internal.h" +#include "network.h" +#include "url.h" + +typedef struct { + URLContext *hd; +} GopherContext; + +static int gopher_write(URLContext *h, const uint8_t *buf, int size) +{ + GopherContext *s = h->priv_data; + return ffurl_write(s->hd, buf, size); +} + +static int gopher_connect(URLContext *h, const char *path) +{ + char buffer[1024]; + + if (!*path) return AVERROR(EINVAL); + switch (*++path) { + case '5': + case '9': + path = strchr(path, '/'); + if (!path) return AVERROR(EINVAL); + break; + default: + av_log(h, AV_LOG_WARNING, + "Gopher protocol type '%c' not supported yet!\n", + *path); + return AVERROR(EINVAL); + } + + /* send gopher sector */ + snprintf(buffer, sizeof(buffer), "%s\r\n", path); + + if (gopher_write(h, buffer, strlen(buffer)) < 0) + return AVERROR(EIO); + + return 0; +} + +static int gopher_close(URLContext *h) +{ + GopherContext *s = h->priv_data; + if (s->hd) { + ffurl_close(s->hd); + s->hd = NULL; + } + return 0; +} + +static int gopher_open(URLContext *h, const char *uri, int flags) +{ + GopherContext *s = h->priv_data; + char hostname[1024], auth[1024], path[1024], buf[1024]; + int port, err; + + h->is_streamed = 1; + + /* needed in any case to build the host string */ + av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, + path, sizeof(path), uri); + + if (port < 0) + port = 70; + + ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL); + + s->hd = NULL; + err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, NULL); + if (err < 0) + goto fail; + + if ((err = gopher_connect(h, path)) < 0) + goto fail; + return 0; + fail: + gopher_close(h); + return err; +} + +static int gopher_read(URLContext *h, uint8_t *buf, int size) +{ + GopherContext *s = h->priv_data; + int len = ffurl_read(s->hd, buf, size); + return len; +} + + +URLProtocol ff_gopher_protocol = { + .name = "gopher", + .url_open = gopher_open, + .url_read = gopher_read, + .url_write = gopher_write, + .url_close = gopher_close, + .priv_data_size = sizeof(GopherContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gopher.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gopher.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/gopher.o libavformat/gopher.o: libavformat/gopher.c \ + libavutil/avstring.h libavutil/attributes.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/network.h config.h libavutil/error.h \ + libavformat/os_support.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gopher.o Binary file ffmpeg/libavformat/gopher.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gsmdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gsmdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,100 @@ +/* + * RAW GSM demuxer + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +#define GSM_BLOCK_SIZE 33 +#define GSM_BLOCK_SAMPLES 160 +#define GSM_SAMPLE_RATE 8000 + +typedef struct { + AVClass *class; + int sample_rate; +} GSMDemuxerContext; + +static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size; + + size = GSM_BLOCK_SIZE; + + pkt->pos = avio_tell(s->pb); + pkt->stream_index = 0; + + ret = av_get_packet(s->pb, pkt, size); + if (ret < GSM_BLOCK_SIZE) { + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR(EIO); + } + pkt->duration = 1; + pkt->pts = pkt->pos / GSM_BLOCK_SIZE; + + return 0; +} + +static int gsm_read_header(AVFormatContext *s) +{ + GSMDemuxerContext *c = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = c->sample_rate; + st->codec->bit_rate = GSM_BLOCK_SIZE * 8 * c->sample_rate / GSM_BLOCK_SAMPLES; + + avpriv_set_pts_info(st, 64, GSM_BLOCK_SAMPLES, GSM_SAMPLE_RATE); + + return 0; +} + +static const AVOption options[] = { + { "sample_rate", "", offsetof(GSMDemuxerContext, sample_rate), + AV_OPT_TYPE_INT, {.i64 = GSM_SAMPLE_RATE}, 1, INT_MAX / GSM_BLOCK_SIZE, + AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass class = { + .class_name = "gsm demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_gsm_demuxer = { + .name = "gsm", + .long_name = NULL_IF_CONFIG_SMALL("raw GSM"), + .priv_data_size = sizeof(GSMDemuxerContext), + .read_header = gsm_read_header, + .read_packet = gsm_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "gsm", + .raw_codec_id = AV_CODEC_ID_GSM, + .priv_class = &class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gsmdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gsmdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/gsmdec.o libavformat/gsmdec.o: libavformat/gsmdec.c \ + libavutil/channel_layout.h libavutil/mathematics.h \ + libavutil/attributes.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gsmdec.o Binary file ffmpeg/libavformat/gsmdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gxf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,596 @@ +/* + * GXF demuxer. + * Copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "avformat.h" +#include "internal.h" +#include "gxf.h" +#include "libavcodec/mpeg12data.h" + +struct gxf_stream_info { + int64_t first_field; + int64_t last_field; + AVRational frames_per_second; + int32_t fields_per_frame; + int64_t track_aux_data; +}; + +/** + * @brief parse gxf timecode and add it to metadata + */ +static int add_timecode_metadata(AVDictionary **pm, const char *key, uint32_t timecode, int fields_per_frame) +{ + char tmp[128]; + int field = timecode & 0xff; + int frame = fields_per_frame ? field / fields_per_frame : field; + int second = (timecode >> 8) & 0xff; + int minute = (timecode >> 16) & 0xff; + int hour = (timecode >> 24) & 0x1f; + int drop = (timecode >> 29) & 1; + // bit 30: color_frame, unused + // ignore invalid time code + if (timecode >> 31) + return 0; + snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d%c%02d", + hour, minute, second, drop ? ';' : ':', frame); + return av_dict_set(pm, key, tmp, 0); +} + +/** + * @brief parses a packet header, extracting type and length + * @param pb AVIOContext to read header from + * @param type detected packet type is stored here + * @param length detected packet length, excluding header is stored here + * @return 0 if header not found or contains invalid data, 1 otherwise + */ +static int parse_packet_header(AVIOContext *pb, GXFPktType *type, int *length) { + if (avio_rb32(pb)) + return 0; + if (avio_r8(pb) != 1) + return 0; + *type = avio_r8(pb); + *length = avio_rb32(pb); + if ((*length >> 24) || *length < 16) + return 0; + *length -= 16; + if (avio_rb32(pb)) + return 0; + if (avio_r8(pb) != 0xe1) + return 0; + if (avio_r8(pb) != 0xe2) + return 0; + return 1; +} + +/** + * @brief check if file starts with a PKT_MAP header + */ +static int gxf_probe(AVProbeData *p) { + static const uint8_t startcode[] = {0, 0, 0, 0, 1, 0xbc}; // start with map packet + static const uint8_t endcode[] = {0, 0, 0, 0, 0xe1, 0xe2}; + if (!memcmp(p->buf, startcode, sizeof(startcode)) && + !memcmp(&p->buf[16 - sizeof(endcode)], endcode, sizeof(endcode))) + return AVPROBE_SCORE_MAX; + return 0; +} + +/** + * @brief gets the stream index for the track with the specified id, creates new + * stream if not found + * @param id id of stream to find / add + * @param format stream format identifier + */ +static int get_sindex(AVFormatContext *s, int id, int format) { + int i; + AVStream *st = NULL; + i = ff_find_stream_index(s, id); + if (i >= 0) + return i; + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->id = id; + switch (format) { + case 3: + case 4: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MJPEG; + break; + case 13: + case 15: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DVVIDEO; + break; + case 14: + case 16: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DVVIDEO; + break; + case 11: + case 12: + case 20: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO; + st->need_parsing = AVSTREAM_PARSE_HEADERS; //get keyframe flag etc. + break; + case 22: + case 23: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG1VIDEO; + st->need_parsing = AVSTREAM_PARSE_HEADERS; //get keyframe flag etc. + break; + case 9: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 48000; + st->codec->bit_rate = 3 * 1 * 48000 * 8; + st->codec->block_align = 3 * 1; + st->codec->bits_per_coded_sample = 24; + break; + case 10: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 48000; + st->codec->bit_rate = 2 * 1 * 48000 * 8; + st->codec->block_align = 2 * 1; + st->codec->bits_per_coded_sample = 16; + break; + case 17: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_AC3; + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = 48000; + break; + // timecode tracks: + case 7: + case 8: + case 24: + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_NONE; + break; + default: + st->codec->codec_type = AVMEDIA_TYPE_UNKNOWN; + st->codec->codec_id = AV_CODEC_ID_NONE; + break; + } + return s->nb_streams - 1; +} + +/** + * @brief filters out interesting tags from material information. + * @param len length of tag section, will be adjusted to contain remaining bytes + * @param si struct to store collected information into + */ +static void gxf_material_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) { + si->first_field = AV_NOPTS_VALUE; + si->last_field = AV_NOPTS_VALUE; + while (*len >= 2) { + GXFMatTag tag = avio_r8(pb); + int tlen = avio_r8(pb); + *len -= 2; + if (tlen > *len) + return; + *len -= tlen; + if (tlen == 4) { + uint32_t value = avio_rb32(pb); + if (tag == MAT_FIRST_FIELD) + si->first_field = value; + else if (tag == MAT_LAST_FIELD) + si->last_field = value; + } else + avio_skip(pb, tlen); + } +} + +static const AVRational frame_rate_tab[] = { + { 60, 1}, + {60000, 1001}, + { 50, 1}, + { 30, 1}, + {30000, 1001}, + { 25, 1}, + { 24, 1}, + {24000, 1001}, + { 0, 0}, +}; + +/** + * @brief convert fps tag value to AVRational fps + * @param fps fps value from tag + * @return fps as AVRational, or 0 / 0 if unknown + */ +static AVRational fps_tag2avr(int32_t fps) { + if (fps < 1 || fps > 9) fps = 9; + return frame_rate_tab[fps - 1]; +} + +/** + * @brief convert UMF attributes flags to AVRational fps + * @param flags UMF flags to convert + * @return fps as AVRational, or 0 / 0 if unknown + */ +static AVRational fps_umf2avr(uint32_t flags) { + static const AVRational map[] = {{50, 1}, {60000, 1001}, {24, 1}, + {25, 1}, {30000, 1001}}; + int idx = av_log2((flags & 0x7c0) >> 6); + return map[idx]; +} + +/** + * @brief filters out interesting tags from track information. + * @param len length of tag section, will be adjusted to contain remaining bytes + * @param si struct to store collected information into + */ +static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) { + si->frames_per_second = (AVRational){0, 0}; + si->fields_per_frame = 0; + si->track_aux_data = 0x80000000; + while (*len >= 2) { + GXFTrackTag tag = avio_r8(pb); + int tlen = avio_r8(pb); + *len -= 2; + if (tlen > *len) + return; + *len -= tlen; + if (tlen == 4) { + uint32_t value = avio_rb32(pb); + if (tag == TRACK_FPS) + si->frames_per_second = fps_tag2avr(value); + else if (tag == TRACK_FPF && (value == 1 || value == 2)) + si->fields_per_frame = value; + } else if (tlen == 8 && tag == TRACK_AUX) + si->track_aux_data = avio_rl64(pb); + else + avio_skip(pb, tlen); + } +} + +/** + * @brief read index from FLT packet into stream 0 av_index + */ +static void gxf_read_index(AVFormatContext *s, int pkt_len) { + AVIOContext *pb = s->pb; + AVStream *st; + uint32_t fields_per_map = avio_rl32(pb); + uint32_t map_cnt = avio_rl32(pb); + int i; + pkt_len -= 8; + if ((s->flags & AVFMT_FLAG_IGNIDX) || !s->streams) { + avio_skip(pb, pkt_len); + return; + } + st = s->streams[0]; + if (map_cnt > 1000) { + av_log(s, AV_LOG_ERROR, "too many index entries %u (%x)\n", map_cnt, map_cnt); + map_cnt = 1000; + } + if (pkt_len < 4 * map_cnt) { + av_log(s, AV_LOG_ERROR, "invalid index length\n"); + avio_skip(pb, pkt_len); + return; + } + pkt_len -= 4 * map_cnt; + av_add_index_entry(st, 0, 0, 0, 0, 0); + for (i = 0; i < map_cnt; i++) + av_add_index_entry(st, (uint64_t)avio_rl32(pb) * 1024, + i * (uint64_t)fields_per_map + 1, 0, 0, 0); + avio_skip(pb, pkt_len); +} + +static int gxf_header(AVFormatContext *s) { + AVIOContext *pb = s->pb; + GXFPktType pkt_type; + int map_len; + int len; + AVRational main_timebase = {0, 0}; + struct gxf_stream_info *si = s->priv_data; + int i; + if (!parse_packet_header(pb, &pkt_type, &map_len) || pkt_type != PKT_MAP) { + av_log(s, AV_LOG_ERROR, "map packet not found\n"); + return 0; + } + map_len -= 2; + if (avio_r8(pb) != 0x0e0 || avio_r8(pb) != 0xff) { + av_log(s, AV_LOG_ERROR, "unknown version or invalid map preamble\n"); + return 0; + } + map_len -= 2; + len = avio_rb16(pb); // length of material data section + if (len > map_len) { + av_log(s, AV_LOG_ERROR, "material data longer than map data\n"); + return 0; + } + map_len -= len; + gxf_material_tags(pb, &len, si); + avio_skip(pb, len); + map_len -= 2; + len = avio_rb16(pb); // length of track description + if (len > map_len) { + av_log(s, AV_LOG_ERROR, "track description longer than map data\n"); + return 0; + } + map_len -= len; + while (len > 0) { + int track_type, track_id, track_len; + AVStream *st; + int idx; + len -= 4; + track_type = avio_r8(pb); + track_id = avio_r8(pb); + track_len = avio_rb16(pb); + len -= track_len; + if (!(track_type & 0x80)) { + av_log(s, AV_LOG_ERROR, "invalid track type %x\n", track_type); + continue; + } + track_type &= 0x7f; + if ((track_id & 0xc0) != 0xc0) { + av_log(s, AV_LOG_ERROR, "invalid track id %x\n", track_id); + continue; + } + track_id &= 0x3f; + gxf_track_tags(pb, &track_len, si); + // check for timecode tracks + if (track_type == 7 || track_type == 8 || track_type == 24) { + add_timecode_metadata(&s->metadata, "timecode", + si->track_aux_data & 0xffffffff, + si->fields_per_frame); + + } + avio_skip(pb, track_len); + + idx = get_sindex(s, track_id, track_type); + if (idx < 0) continue; + st = s->streams[idx]; + if (!main_timebase.num || !main_timebase.den) { + main_timebase.num = si->frames_per_second.den; + main_timebase.den = si->frames_per_second.num * 2; + } + st->start_time = si->first_field; + if (si->first_field != AV_NOPTS_VALUE && si->last_field != AV_NOPTS_VALUE) + st->duration = si->last_field - si->first_field; + } + if (len < 0) + av_log(s, AV_LOG_ERROR, "invalid track description length specified\n"); + if (map_len) + avio_skip(pb, map_len); + if (!parse_packet_header(pb, &pkt_type, &len)) { + av_log(s, AV_LOG_ERROR, "sync lost in header\n"); + return -1; + } + if (pkt_type == PKT_FLT) { + gxf_read_index(s, len); + if (!parse_packet_header(pb, &pkt_type, &len)) { + av_log(s, AV_LOG_ERROR, "sync lost in header\n"); + return -1; + } + } + if (pkt_type == PKT_UMF) { + if (len >= 0x39) { + AVRational fps; + len -= 0x39; + avio_skip(pb, 5); // preamble + avio_skip(pb, 0x30); // payload description + fps = fps_umf2avr(avio_rl32(pb)); + if (!main_timebase.num || !main_timebase.den) { + av_log(s, AV_LOG_WARNING, "No FPS track tag, using UMF fps tag." + " This might give wrong results.\n"); + // this may not always be correct, but simply the best we can get + main_timebase.num = fps.den; + main_timebase.den = fps.num * 2; + } + + if (len >= 0x18) { + len -= 0x18; + avio_skip(pb, 0x10); + add_timecode_metadata(&s->metadata, "timecode_at_mark_in", + avio_rl32(pb), si->fields_per_frame); + add_timecode_metadata(&s->metadata, "timecode_at_mark_out", + avio_rl32(pb), si->fields_per_frame); + } + } else + av_log(s, AV_LOG_INFO, "UMF packet too short\n"); + } else + av_log(s, AV_LOG_INFO, "UMF packet missing\n"); + avio_skip(pb, len); + // set a fallback value, 60000/1001 is specified for audio-only files + // so use that regardless of why we do not know the video frame rate. + if (!main_timebase.num || !main_timebase.den) + main_timebase = (AVRational){1001, 60000}; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + avpriv_set_pts_info(st, 32, main_timebase.num, main_timebase.den); + } + return 0; +} + +#define READ_ONE() \ + { \ + if (!max_interval-- || url_feof(pb)) \ + goto out; \ + tmp = tmp << 8 | avio_r8(pb); \ + } + +/** + * @brief resync the stream on the next media packet with specified properties + * @param max_interval how many bytes to search for matching packet at most + * @param track track id the media packet must belong to, -1 for any + * @param timestamp minimum timestamp (== field number) the packet must have, -1 for any + * @return timestamp of packet found + */ +static int64_t gxf_resync_media(AVFormatContext *s, uint64_t max_interval, int track, int timestamp) { + uint32_t tmp; + uint64_t last_pos; + uint64_t last_found_pos = 0; + int cur_track; + int64_t cur_timestamp = AV_NOPTS_VALUE; + int len; + AVIOContext *pb = s->pb; + GXFPktType type; + tmp = avio_rb32(pb); +start: + while (tmp) + READ_ONE(); + READ_ONE(); + if (tmp != 1) + goto start; + last_pos = avio_tell(pb); + if (avio_seek(pb, -5, SEEK_CUR) < 0) + goto out; + if (!parse_packet_header(pb, &type, &len) || type != PKT_MEDIA) { + if (avio_seek(pb, last_pos, SEEK_SET) < 0) + goto out; + goto start; + } + avio_r8(pb); + cur_track = avio_r8(pb); + cur_timestamp = avio_rb32(pb); + last_found_pos = avio_tell(pb) - 16 - 6; + if ((track >= 0 && track != cur_track) || (timestamp >= 0 && timestamp > cur_timestamp)) { + if (avio_seek(pb, last_pos, SEEK_SET) >= 0) + goto start; + } +out: + if (last_found_pos) + avio_seek(pb, last_found_pos, SEEK_SET); + return cur_timestamp; +} + +static int gxf_packet(AVFormatContext *s, AVPacket *pkt) { + AVIOContext *pb = s->pb; + GXFPktType pkt_type; + int pkt_len; + struct gxf_stream_info *si = s->priv_data; + + while (!pb->eof_reached) { + AVStream *st; + int track_type, track_id, ret; + int field_nr, field_info, skip = 0; + int stream_index; + if (!parse_packet_header(pb, &pkt_type, &pkt_len)) { + if (!url_feof(pb)) + av_log(s, AV_LOG_ERROR, "sync lost\n"); + return -1; + } + if (pkt_type == PKT_FLT) { + gxf_read_index(s, pkt_len); + continue; + } + if (pkt_type != PKT_MEDIA) { + avio_skip(pb, pkt_len); + continue; + } + if (pkt_len < 16) { + av_log(s, AV_LOG_ERROR, "invalid media packet length\n"); + continue; + } + pkt_len -= 16; + track_type = avio_r8(pb); + track_id = avio_r8(pb); + stream_index = get_sindex(s, track_id, track_type); + if (stream_index < 0) + return stream_index; + st = s->streams[stream_index]; + field_nr = avio_rb32(pb); + field_info = avio_rb32(pb); + avio_rb32(pb); // "timeline" field number + avio_r8(pb); // flags + avio_r8(pb); // reserved + if (st->codec->codec_id == AV_CODEC_ID_PCM_S24LE || + st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { + int first = field_info >> 16; + int last = field_info & 0xffff; // last is exclusive + int bps = av_get_bits_per_sample(st->codec->codec_id)>>3; + if (first <= last && last*bps <= pkt_len) { + avio_skip(pb, first*bps); + skip = pkt_len - last*bps; + pkt_len = (last-first)*bps; + } else + av_log(s, AV_LOG_ERROR, "invalid first and last sample values\n"); + } + ret = av_get_packet(pb, pkt, pkt_len); + if (skip) + avio_skip(pb, skip); + pkt->stream_index = stream_index; + pkt->dts = field_nr; + + //set duration manually for DV or else lavf misdetects the frame rate + if (st->codec->codec_id == AV_CODEC_ID_DVVIDEO) + pkt->duration = si->fields_per_frame; + + return ret; + } + return AVERROR_EOF; +} + +static int gxf_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { + int res = 0; + uint64_t pos; + uint64_t maxlen = 100 * 1024 * 1024; + AVStream *st = s->streams[0]; + int64_t start_time = s->streams[stream_index]->start_time; + int64_t found; + int idx; + if (timestamp < start_time) timestamp = start_time; + idx = av_index_search_timestamp(st, timestamp - start_time, + AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); + if (idx < 0) + return -1; + pos = st->index_entries[idx].pos; + if (idx < st->nb_index_entries - 2) + maxlen = st->index_entries[idx + 2].pos - pos; + maxlen = FFMAX(maxlen, 200 * 1024); + res = avio_seek(s->pb, pos, SEEK_SET); + if (res < 0) + return res; + found = gxf_resync_media(s, maxlen, -1, timestamp); + if (FFABS(found - timestamp) > 4) + return -1; + return 0; +} + +static int64_t gxf_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit) { + AVIOContext *pb = s->pb; + int64_t res; + if (avio_seek(pb, *pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + res = gxf_resync_media(s, pos_limit - *pos, -1, -1); + *pos = avio_tell(pb); + return res; +} + +AVInputFormat ff_gxf_demuxer = { + .name = "gxf", + .long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), + .priv_data_size = sizeof(struct gxf_stream_info), + .read_probe = gxf_probe, + .read_header = gxf_header, + .read_packet = gxf_packet, + .read_seek = gxf_seek, + .read_timestamp = gxf_read_timestamp, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gxf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/gxf.o libavformat/gxf.o: libavformat/gxf.c \ + libavutil/channel_layout.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/gxf.h \ + libavcodec/mpeg12data.h libavcodec/rl.h libavcodec/get_bits.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gxf.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,52 @@ +/* + * GXF demuxer + * copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_GXF_H +#define AVFORMAT_GXF_H + +typedef enum { + PKT_MAP = 0xbc, + PKT_MEDIA = 0xbf, + PKT_EOS = 0xfb, + PKT_FLT = 0xfc, + PKT_UMF = 0xfd, +} GXFPktType; + +typedef enum { + MAT_NAME = 0x40, + MAT_FIRST_FIELD = 0x41, + MAT_LAST_FIELD = 0x42, + MAT_MARK_IN = 0x43, + MAT_MARK_OUT = 0x44, + MAT_SIZE = 0x45, +} GXFMatTag; + +typedef enum { + TRACK_NAME = 0x4c, + TRACK_AUX = 0x4d, + TRACK_VER = 0x4e, + TRACK_MPG_AUX = 0x4f, + TRACK_FPS = 0x50, + TRACK_LINES = 0x51, + TRACK_FPF = 0x52, +} GXFTrackTag; + +#endif /* AVFORMAT_GXF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxf.o Binary file ffmpeg/libavformat/gxf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxfenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gxfenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,997 @@ +/* + * GXF muxer. + * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avassert.h" +#include "libavutil/intfloat.h" +#include "libavutil/opt.h" +#include "libavutil/mathematics.h" +#include "libavutil/timecode.h" +#include "avformat.h" +#include "internal.h" +#include "gxf.h" +#include "audiointerleave.h" + +#define GXF_AUDIO_PACKET_SIZE 65536 + +#define GXF_TIMECODE(c, d, h, m, s, f) \ + ((c) << 30 | (d) << 29 | (h) << 24 | (m) << 16 | (s) << 8 | (f)) + +typedef struct GXFTimecode{ + int hh; + int mm; + int ss; + int ff; + int color; + int drop; +} GXFTimecode; + +typedef struct GXFStreamContext { + AudioInterleaveContext aic; + uint32_t track_type; + uint32_t sample_size; + uint32_t sample_rate; + uint16_t media_type; + uint16_t media_info; + int frame_rate_index; + int lines_index; + int fields; + int iframes; + int pframes; + int bframes; + int p_per_gop; + int b_per_i_or_p; ///< number of B frames per I frame or P frame + int first_gop_closed; + unsigned order; ///< interleaving order +} GXFStreamContext; + +typedef struct GXFContext { + AVClass *av_class; + uint32_t nb_fields; + uint16_t audio_tracks; + uint16_t mpeg_tracks; + int64_t creation_time; + uint32_t umf_start_offset; + uint32_t umf_track_offset; + uint32_t umf_media_offset; + uint32_t umf_length; + uint16_t umf_track_size; + uint16_t umf_media_size; + AVRational time_base; + int flags; + GXFStreamContext timecode_track; + unsigned *flt_entries; ///< offsets of packets /1024, starts after 2nd video field + unsigned flt_entries_nb; + uint64_t *map_offsets; ///< offset of map packets + unsigned map_offsets_nb; + unsigned packet_count; + GXFTimecode tc; +} GXFContext; + +static const struct { + int height, index; +} gxf_lines_tab[] = { + { 480, 1 }, /* NTSC */ + { 512, 1 }, /* NTSC + VBI */ + { 576, 2 }, /* PAL */ + { 608, 2 }, /* PAL + VBI */ + { 1080, 4 }, + { 720, 6 }, +}; + +static const AVCodecTag gxf_media_types[] = { + { AV_CODEC_ID_MJPEG , 3 }, /* NTSC */ + { AV_CODEC_ID_MJPEG , 4 }, /* PAL */ + { AV_CODEC_ID_PCM_S24LE , 9 }, + { AV_CODEC_ID_PCM_S16LE , 10 }, + { AV_CODEC_ID_MPEG2VIDEO, 11 }, /* NTSC */ + { AV_CODEC_ID_MPEG2VIDEO, 12 }, /* PAL */ + { AV_CODEC_ID_DVVIDEO , 13 }, /* NTSC */ + { AV_CODEC_ID_DVVIDEO , 14 }, /* PAL */ + { AV_CODEC_ID_DVVIDEO , 15 }, /* 50M NTSC */ + { AV_CODEC_ID_DVVIDEO , 16 }, /* 50M PAL */ + { AV_CODEC_ID_AC3 , 17 }, + //{ AV_CODEC_ID_NONE, , 18 }, /* Non compressed 24 bit audio */ + { AV_CODEC_ID_MPEG2VIDEO, 20 }, /* MPEG HD */ + { AV_CODEC_ID_MPEG1VIDEO, 22 }, /* NTSC */ + { AV_CODEC_ID_MPEG1VIDEO, 23 }, /* PAL */ + { AV_CODEC_ID_NONE, 0 }, +}; + +#define SERVER_PATH "EXT:/PDR/default/" +#define ES_NAME_PATTERN "EXT:/PDR/default/ES." + +static int gxf_find_lines_index(AVStream *st) +{ + GXFStreamContext *sc = st->priv_data; + int i; + + for (i = 0; i < 6; ++i) { + if (st->codec->height == gxf_lines_tab[i].height) { + sc->lines_index = gxf_lines_tab[i].index; + return 0; + } + } + return -1; +} + +static void gxf_write_padding(AVIOContext *pb, int64_t to_pad) +{ + for (; to_pad > 0; to_pad--) { + avio_w8(pb, 0); + } +} + +static int64_t updatePacketSize(AVIOContext *pb, int64_t pos) +{ + int64_t curpos; + int size; + + size = avio_tell(pb) - pos; + if (size % 4) { + gxf_write_padding(pb, 4 - size % 4); + size = avio_tell(pb) - pos; + } + curpos = avio_tell(pb); + avio_seek(pb, pos + 6, SEEK_SET); + avio_wb32(pb, size); + avio_seek(pb, curpos, SEEK_SET); + return curpos - pos; +} + +static int64_t updateSize(AVIOContext *pb, int64_t pos) +{ + int64_t curpos; + + curpos = avio_tell(pb); + avio_seek(pb, pos, SEEK_SET); + avio_wb16(pb, curpos - pos - 2); + avio_seek(pb, curpos, SEEK_SET); + return curpos - pos; +} + +static void gxf_write_packet_header(AVIOContext *pb, GXFPktType type) +{ + avio_wb32(pb, 0); /* packet leader for synchro */ + avio_w8(pb, 1); + avio_w8(pb, type); /* map packet */ + avio_wb32(pb, 0); /* size */ + avio_wb32(pb, 0); /* reserved */ + avio_w8(pb, 0xE1); /* trailer 1 */ + avio_w8(pb, 0xE2); /* trailer 2 */ +} + +static int gxf_write_mpeg_auxiliary(AVIOContext *pb, AVStream *st) +{ + GXFStreamContext *sc = st->priv_data; + char buffer[1024]; + int size, starting_line; + + if (sc->iframes) { + sc->p_per_gop = sc->pframes / sc->iframes; + if (sc->pframes % sc->iframes) + sc->p_per_gop++; + if (sc->pframes) { + sc->b_per_i_or_p = sc->bframes / sc->pframes; + if (sc->bframes % sc->pframes) + sc->b_per_i_or_p++; + } + if (sc->p_per_gop > 9) + sc->p_per_gop = 9; /* ensure value won't take more than one char */ + if (sc->b_per_i_or_p > 9) + sc->b_per_i_or_p = 9; /* ensure value won't take more than one char */ + } + if (st->codec->height == 512 || st->codec->height == 608) + starting_line = 7; // VBI + else if (st->codec->height == 480) + starting_line = 20; + else + starting_line = 23; // default PAL + + size = snprintf(buffer, sizeof(buffer), "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n" + "Pix 0\nCf %d\nCg %d\nSl %d\nnl16 %d\nVi 1\nf1 1\n", + (float)st->codec->bit_rate, sc->p_per_gop, sc->b_per_i_or_p, + st->codec->pix_fmt == AV_PIX_FMT_YUV422P ? 2 : 1, sc->first_gop_closed == 1, + starting_line, (st->codec->height + 15) / 16); + av_assert0(size < sizeof(buffer)); + avio_w8(pb, TRACK_MPG_AUX); + avio_w8(pb, size + 1); + avio_write(pb, (uint8_t *)buffer, size + 1); + return size + 3; +} + +static int gxf_write_timecode_auxiliary(AVIOContext *pb, GXFContext *gxf) +{ + uint32_t timecode = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + avio_wl32(pb, timecode); + /* reserved */ + avio_wl32(pb, 0); + return 8; +} + +static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, int index) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int mpeg = sc->track_type == 4 || sc->track_type == 9; + + /* track description section */ + avio_w8(pb, sc->media_type + 0x80); + avio_w8(pb, index + 0xC0); + + pos = avio_tell(pb); + avio_wb16(pb, 0); /* size */ + + /* media file name */ + avio_w8(pb, TRACK_NAME); + avio_w8(pb, strlen(ES_NAME_PATTERN) + 3); + avio_write(pb, ES_NAME_PATTERN, sizeof(ES_NAME_PATTERN) - 1); + avio_wb16(pb, sc->media_info); + avio_w8(pb, 0); + + if (!mpeg) { + /* auxiliary information */ + avio_w8(pb, TRACK_AUX); + avio_w8(pb, 8); + if (sc->track_type == 3) + gxf_write_timecode_auxiliary(pb, gxf); + else + avio_wl64(pb, 0); + } + + /* file system version */ + avio_w8(pb, TRACK_VER); + avio_w8(pb, 4); + avio_wb32(pb, 0); + + if (mpeg) + gxf_write_mpeg_auxiliary(pb, s->streams[index]); + + /* frame rate */ + avio_w8(pb, TRACK_FPS); + avio_w8(pb, 4); + avio_wb32(pb, sc->frame_rate_index); + + /* lines per frame */ + avio_w8(pb, TRACK_LINES); + avio_w8(pb, 4); + avio_wb32(pb, sc->lines_index); + + /* fields per frame */ + avio_w8(pb, TRACK_FPF); + avio_w8(pb, 4); + avio_wb32(pb, sc->fields); + + return updateSize(pb, pos); +} + +static int gxf_write_material_data_section(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int len; + const char *filename = strrchr(s->filename, '/'); + + pos = avio_tell(pb); + avio_wb16(pb, 0); /* size */ + + /* name */ + if (filename) + filename++; + else + filename = s->filename; + len = strlen(filename); + + avio_w8(pb, MAT_NAME); + avio_w8(pb, strlen(SERVER_PATH) + len + 1); + avio_write(pb, SERVER_PATH, sizeof(SERVER_PATH) - 1); + avio_write(pb, filename, len); + avio_w8(pb, 0); + + /* first field */ + avio_w8(pb, MAT_FIRST_FIELD); + avio_w8(pb, 4); + avio_wb32(pb, 0); + + /* last field */ + avio_w8(pb, MAT_LAST_FIELD); + avio_w8(pb, 4); + avio_wb32(pb, gxf->nb_fields); + + /* reserved */ + avio_w8(pb, MAT_MARK_IN); + avio_w8(pb, 4); + avio_wb32(pb, 0); + + avio_w8(pb, MAT_MARK_OUT); + avio_w8(pb, 4); + avio_wb32(pb, gxf->nb_fields); + + /* estimated size */ + avio_w8(pb, MAT_SIZE); + avio_w8(pb, 4); + avio_wb32(pb, avio_size(pb) / 1024); + + return updateSize(pb, pos); +} + +static int gxf_write_track_description_section(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int i; + + pos = avio_tell(pb); + avio_wb16(pb, 0); /* size */ + for (i = 0; i < s->nb_streams; ++i) + gxf_write_track_description(s, s->streams[i]->priv_data, i); + + gxf_write_track_description(s, &gxf->timecode_track, s->nb_streams); + + return updateSize(pb, pos); +} + +static int gxf_write_map_packet(AVFormatContext *s, int rewrite) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos = avio_tell(pb); + + if (!rewrite) { + if (!(gxf->map_offsets_nb % 30)) { + gxf->map_offsets = av_realloc_f(gxf->map_offsets, + sizeof(*gxf->map_offsets), + gxf->map_offsets_nb+30); + if (!gxf->map_offsets) { + av_log(s, AV_LOG_ERROR, "could not realloc map offsets\n"); + return -1; + } + } + gxf->map_offsets[gxf->map_offsets_nb++] = pos; // do not increment here + } + + gxf_write_packet_header(pb, PKT_MAP); + + /* preamble */ + avio_w8(pb, 0xE0); /* version */ + avio_w8(pb, 0xFF); /* reserved */ + + gxf_write_material_data_section(s); + gxf_write_track_description_section(s); + + return updatePacketSize(pb, pos); +} + +static int gxf_write_flt_packet(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos = avio_tell(pb); + int fields_per_flt = (gxf->nb_fields+1) / 1000 + 1; + int flt_entries = gxf->nb_fields / fields_per_flt; + int i = 0; + + gxf_write_packet_header(pb, PKT_FLT); + + avio_wl32(pb, fields_per_flt); /* number of fields */ + avio_wl32(pb, flt_entries); /* number of active flt entries */ + + if (gxf->flt_entries) { + for (i = 0; i < flt_entries; i++) + avio_wl32(pb, gxf->flt_entries[(i*fields_per_flt)>>1]); + } + + for (; i < 1000; i++) + avio_wl32(pb, 0); + + return updatePacketSize(pb, pos); +} + +static int gxf_write_umf_material_description(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int timecode_base = gxf->time_base.den == 60000 ? 60 : 50; + int64_t timestamp = 0; + AVDictionaryEntry *t; + uint64_t nb_fields; + uint32_t timecode_in; // timecode at mark in + uint32_t timecode_out; // timecode at mark out + + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) + timestamp = ff_iso8601_to_unix_time(t->value); + + timecode_in = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + gxf->tc.hh, gxf->tc.mm, + gxf->tc.ss, gxf->tc.ff); + + nb_fields = gxf->nb_fields + + gxf->tc.hh * (timecode_base * 3600) + + gxf->tc.mm * (timecode_base * 60) + + gxf->tc.ss * timecode_base + + gxf->tc.ff; + + timecode_out = GXF_TIMECODE(gxf->tc.color, gxf->tc.drop, + nb_fields / (timecode_base * 3600) % 24, + nb_fields / (timecode_base * 60) % 60, + nb_fields / timecode_base % 60, + nb_fields % timecode_base); + + avio_wl32(pb, gxf->flags); + avio_wl32(pb, gxf->nb_fields); /* length of the longest track */ + avio_wl32(pb, gxf->nb_fields); /* length of the shortest track */ + avio_wl32(pb, 0); /* mark in */ + avio_wl32(pb, gxf->nb_fields); /* mark out */ + avio_wl32(pb, timecode_in); /* timecode mark in */ + avio_wl32(pb, timecode_out); /* timecode mark out */ + avio_wl64(pb, timestamp); /* modification time */ + avio_wl64(pb, timestamp); /* creation time */ + avio_wl16(pb, 0); /* reserved */ + avio_wl16(pb, 0); /* reserved */ + avio_wl16(pb, gxf->audio_tracks); + avio_wl16(pb, 1); /* timecode track count */ + avio_wl16(pb, 0); /* reserved */ + avio_wl16(pb, gxf->mpeg_tracks); + return 48; +} + +static int gxf_write_umf_payload(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + + avio_wl32(pb, gxf->umf_length); /* total length of the umf data */ + avio_wl32(pb, 3); /* version */ + avio_wl32(pb, s->nb_streams+1); + avio_wl32(pb, gxf->umf_track_offset); /* umf track section offset */ + avio_wl32(pb, gxf->umf_track_size); + avio_wl32(pb, s->nb_streams+1); + avio_wl32(pb, gxf->umf_media_offset); + avio_wl32(pb, gxf->umf_media_size); + avio_wl32(pb, gxf->umf_length); /* user data offset */ + avio_wl32(pb, 0); /* user data size */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + return 48; +} + +static int gxf_write_umf_track_description(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + GXFContext *gxf = s->priv_data; + int64_t pos = avio_tell(pb); + int i; + + gxf->umf_track_offset = pos - gxf->umf_start_offset; + for (i = 0; i < s->nb_streams; ++i) { + GXFStreamContext *sc = s->streams[i]->priv_data; + avio_wl16(pb, sc->media_info); + avio_wl16(pb, 1); + } + + avio_wl16(pb, gxf->timecode_track.media_info); + avio_wl16(pb, 1); + + return avio_tell(pb) - pos; +} + +static int gxf_write_umf_media_mpeg(AVIOContext *pb, AVStream *st) +{ + GXFStreamContext *sc = st->priv_data; + + if (st->codec->pix_fmt == AV_PIX_FMT_YUV422P) + avio_wl32(pb, 2); + else + avio_wl32(pb, 1); /* default to 420 */ + avio_wl32(pb, sc->first_gop_closed == 1); /* closed = 1, open = 0, unknown = 255 */ + avio_wl32(pb, 3); /* top = 1, bottom = 2, frame = 3, unknown = 0 */ + avio_wl32(pb, 1); /* I picture per GOP */ + avio_wl32(pb, sc->p_per_gop); + avio_wl32(pb, sc->b_per_i_or_p); + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) + avio_wl32(pb, 2); + else if (st->codec->codec_id == AV_CODEC_ID_MPEG1VIDEO) + avio_wl32(pb, 1); + else + avio_wl32(pb, 0); + avio_wl32(pb, 0); /* reserved */ + return 32; +} + +static int gxf_write_umf_media_timecode(AVIOContext *pb, int drop) +{ + avio_wl32(pb, drop); /* drop frame */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + return 32; +} + +static int gxf_write_umf_media_dv(AVIOContext *pb, GXFStreamContext *sc) +{ + int i; + + for (i = 0; i < 8; i++) { + avio_wb32(pb, 0); + } + return 32; +} + +static int gxf_write_umf_media_audio(AVIOContext *pb, GXFStreamContext *sc) +{ + avio_wl64(pb, av_double2int(1)); /* sound level to begin to */ + avio_wl64(pb, av_double2int(1)); /* sound level to begin to */ + avio_wl32(pb, 0); /* number of fields over which to ramp up sound level */ + avio_wl32(pb, 0); /* number of fields over which to ramp down sound level */ + avio_wl32(pb, 0); /* reserved */ + avio_wl32(pb, 0); /* reserved */ + return 32; +} + +static int gxf_write_umf_media_description(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int i, j; + + pos = avio_tell(pb); + gxf->umf_media_offset = pos - gxf->umf_start_offset; + for (i = 0; i <= s->nb_streams; ++i) { + GXFStreamContext *sc; + int64_t startpos, curpos; + + if (i == s->nb_streams) + sc = &gxf->timecode_track; + else + sc = s->streams[i]->priv_data; + + startpos = avio_tell(pb); + avio_wl16(pb, 0); /* length */ + avio_wl16(pb, sc->media_info); + avio_wl16(pb, 0); /* reserved */ + avio_wl16(pb, 0); /* reserved */ + avio_wl32(pb, gxf->nb_fields); + avio_wl32(pb, 0); /* attributes rw, ro */ + avio_wl32(pb, 0); /* mark in */ + avio_wl32(pb, gxf->nb_fields); /* mark out */ + avio_write(pb, ES_NAME_PATTERN, strlen(ES_NAME_PATTERN)); + avio_wb16(pb, sc->media_info); + for (j = strlen(ES_NAME_PATTERN)+2; j < 88; j++) + avio_w8(pb, 0); + avio_wl32(pb, sc->track_type); + avio_wl32(pb, sc->sample_rate); + avio_wl32(pb, sc->sample_size); + avio_wl32(pb, 0); /* reserved */ + + if (sc == &gxf->timecode_track) + gxf_write_umf_media_timecode(pb, gxf->tc.drop); + else { + AVStream *st = s->streams[i]; + switch (st->codec->codec_id) { + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + gxf_write_umf_media_mpeg(pb, st); + break; + case AV_CODEC_ID_PCM_S16LE: + gxf_write_umf_media_audio(pb, sc); + break; + case AV_CODEC_ID_DVVIDEO: + gxf_write_umf_media_dv(pb, sc); + break; + } + } + + curpos = avio_tell(pb); + avio_seek(pb, startpos, SEEK_SET); + avio_wl16(pb, curpos - startpos); + avio_seek(pb, curpos, SEEK_SET); + } + return avio_tell(pb) - pos; +} + +static int gxf_write_umf_packet(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos = avio_tell(pb); + + gxf_write_packet_header(pb, PKT_UMF); + + /* preamble */ + avio_w8(pb, 3); /* first and last (only) packet */ + avio_wb32(pb, gxf->umf_length); /* data length */ + + gxf->umf_start_offset = avio_tell(pb); + gxf_write_umf_payload(s); + gxf_write_umf_material_description(s); + gxf->umf_track_size = gxf_write_umf_track_description(s); + gxf->umf_media_size = gxf_write_umf_media_description(s); + gxf->umf_length = avio_tell(pb) - gxf->umf_start_offset; + return updatePacketSize(pb, pos); +} + +static const int GXF_samples_per_frame[] = { 32768, 0 }; + +static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) +{ + if (!vsc) + return; + + sc->media_type = vsc->sample_rate == 60 ? 7 : 8; + sc->sample_rate = vsc->sample_rate; + sc->media_info = ('T'<<8) | '0'; + sc->track_type = 3; + sc->frame_rate_index = vsc->frame_rate_index; + sc->lines_index = vsc->lines_index; + sc->sample_size = 16; + sc->fields = vsc->fields; +} + +static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, const char *tcstr, int fields) +{ + char c; + + if (sscanf(tcstr, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) { + av_log(s, AV_LOG_ERROR, "unable to parse timecode, " + "syntax: hh:mm:ss[:;.]ff\n"); + return -1; + } + + tc->color = 0; + tc->drop = c != ':'; + + if (fields == 2) + tc->ff = tc->ff * 2; + + return 0; +} + +static int gxf_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + GXFContext *gxf = s->priv_data; + GXFStreamContext *vsc = NULL; + uint8_t tracks[255] = {0}; + int i, media_info = 0; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + + if (!pb->seekable) { + av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome\n"); + return -1; + } + + gxf->flags |= 0x00080000; /* material is simple clip */ + for (i = 0; i < s->nb_streams; ++i) { + AVStream *st = s->streams[i]; + GXFStreamContext *sc = av_mallocz(sizeof(*sc)); + if (!sc) + return AVERROR(ENOMEM); + st->priv_data = sc; + + sc->media_type = ff_codec_get_tag(gxf_media_types, st->codec->codec_id); + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st->codec->codec_id != AV_CODEC_ID_PCM_S16LE) { + av_log(s, AV_LOG_ERROR, "only 16 BIT PCM LE allowed for now\n"); + return -1; + } + if (st->codec->sample_rate != 48000) { + av_log(s, AV_LOG_ERROR, "only 48000hz sampling rate is allowed\n"); + return -1; + } + if (st->codec->channels != 1) { + av_log(s, AV_LOG_ERROR, "only mono tracks are allowed\n"); + return -1; + } + sc->track_type = 2; + sc->sample_rate = st->codec->sample_rate; + avpriv_set_pts_info(st, 64, 1, sc->sample_rate); + sc->sample_size = 16; + sc->frame_rate_index = -2; + sc->lines_index = -2; + sc->fields = -2; + gxf->audio_tracks++; + gxf->flags |= 0x04000000; /* audio is 16 bit pcm */ + media_info = 'A'; + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (i != 0) { + av_log(s, AV_LOG_ERROR, "video stream must be the first track\n"); + return -1; + } + /* FIXME check from time_base ? */ + if (st->codec->height == 480 || st->codec->height == 512) { /* NTSC or NTSC+VBI */ + sc->frame_rate_index = 5; + sc->sample_rate = 60; + gxf->flags |= 0x00000080; + gxf->time_base = (AVRational){ 1001, 60000 }; + } else if (st->codec->height == 576 || st->codec->height == 608) { /* PAL or PAL+VBI */ + sc->frame_rate_index = 6; + sc->media_type++; + sc->sample_rate = 50; + gxf->flags |= 0x00000040; + gxf->time_base = (AVRational){ 1, 50 }; + } else { + av_log(s, AV_LOG_ERROR, "unsupported video resolution, " + "gxf muxer only accepts PAL or NTSC resolutions currently\n"); + return -1; + } + if (!tcr) + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den); + if (gxf_find_lines_index(st) < 0) + sc->lines_index = -1; + sc->sample_size = st->codec->bit_rate; + sc->fields = 2; /* interlaced */ + + vsc = sc; + + switch (st->codec->codec_id) { + case AV_CODEC_ID_MJPEG: + sc->track_type = 1; + gxf->flags |= 0x00004000; + media_info = 'J'; + break; + case AV_CODEC_ID_MPEG1VIDEO: + sc->track_type = 9; + gxf->mpeg_tracks++; + media_info = 'L'; + break; + case AV_CODEC_ID_MPEG2VIDEO: + sc->first_gop_closed = -1; + sc->track_type = 4; + gxf->mpeg_tracks++; + gxf->flags |= 0x00008000; + media_info = 'M'; + break; + case AV_CODEC_ID_DVVIDEO: + if (st->codec->pix_fmt == AV_PIX_FMT_YUV422P) { + sc->media_type += 2; + sc->track_type = 6; + gxf->flags |= 0x00002000; + media_info = 'E'; + } else { + sc->track_type = 5; + gxf->flags |= 0x00001000; + media_info = 'D'; + } + break; + default: + av_log(s, AV_LOG_ERROR, "video codec not supported\n"); + return -1; + } + } + /* FIXME first 10 audio tracks are 0 to 9 next 22 are A to V */ + sc->media_info = media_info<<8 | ('0'+tracks[media_info]++); + sc->order = s->nb_streams - st->index; + } + + if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0) + return -1; + + if (tcr && vsc) + gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields); + + gxf_init_timecode_track(&gxf->timecode_track, vsc); + gxf->flags |= 0x200000; // time code track is non-drop frame + + gxf_write_map_packet(s, 0); + gxf_write_flt_packet(s); + gxf_write_umf_packet(s); + + gxf->packet_count = 3; + + avio_flush(pb); + return 0; +} + +static int gxf_write_eos_packet(AVIOContext *pb) +{ + int64_t pos = avio_tell(pb); + + gxf_write_packet_header(pb, PKT_EOS); + return updatePacketSize(pb, pos); +} + +static int gxf_write_trailer(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t end; + int i; + + ff_audio_interleave_close(s); + + gxf_write_eos_packet(pb); + end = avio_tell(pb); + avio_seek(pb, 0, SEEK_SET); + /* overwrite map, flt and umf packets with new values */ + gxf_write_map_packet(s, 1); + gxf_write_flt_packet(s); + gxf_write_umf_packet(s); + avio_flush(pb); + /* update duration in all map packets */ + for (i = 1; i < gxf->map_offsets_nb; i++) { + avio_seek(pb, gxf->map_offsets[i], SEEK_SET); + gxf_write_map_packet(s, 1); + avio_flush(pb); + } + + avio_seek(pb, end, SEEK_SET); + + av_freep(&gxf->flt_entries); + av_freep(&gxf->map_offsets); + + return 0; +} + +static int gxf_parse_mpeg_frame(GXFStreamContext *sc, const uint8_t *buf, int size) +{ + uint32_t c=-1; + int i; + for(i=0; ifirst_gop_closed == -1) /* GOP start code */ + sc->first_gop_closed= (buf[i+4]>>6)&1; + } + return (buf[i+1]>>3)&7; +} + +static int gxf_write_media_preamble(AVFormatContext *s, AVPacket *pkt, int size) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[pkt->stream_index]; + GXFStreamContext *sc = st->priv_data; + unsigned field_nb; + /* If the video is frame-encoded, the frame numbers shall be represented by + * even field numbers. + * see SMPTE360M-2004 6.4.2.1.3 Media field number */ + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + field_nb = gxf->nb_fields; + } else { + field_nb = av_rescale_rnd(pkt->dts, gxf->time_base.den, + (int64_t)48000*gxf->time_base.num, AV_ROUND_UP); + } + + avio_w8(pb, sc->media_type); + avio_w8(pb, st->index); + avio_wb32(pb, field_nb); + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + avio_wb16(pb, 0); + avio_wb16(pb, size / 2); + } else if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + int frame_type = gxf_parse_mpeg_frame(sc, pkt->data, pkt->size); + if (frame_type == AV_PICTURE_TYPE_I) { + avio_w8(pb, 0x0d); + sc->iframes++; + } else if (frame_type == AV_PICTURE_TYPE_B) { + avio_w8(pb, 0x0f); + sc->bframes++; + } else { + avio_w8(pb, 0x0e); + sc->pframes++; + } + avio_wb24(pb, size); + } else if (st->codec->codec_id == AV_CODEC_ID_DVVIDEO) { + avio_w8(pb, size / 4096); + avio_wb24(pb, 0); + } else + avio_wb32(pb, size); + avio_wb32(pb, field_nb); + avio_w8(pb, 1); /* flags */ + avio_w8(pb, 0); /* reserved */ + return 16; +} + +static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + GXFContext *gxf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[pkt->stream_index]; + int64_t pos = avio_tell(pb); + int padding = 0; + int packet_start_offset = avio_tell(pb) / 1024; + + gxf_write_packet_header(pb, PKT_MEDIA); + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO && pkt->size % 4) /* MPEG-2 frames must be padded */ + padding = 4 - pkt->size % 4; + else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + padding = GXF_AUDIO_PACKET_SIZE - pkt->size; + gxf_write_media_preamble(s, pkt, pkt->size + padding); + avio_write(pb, pkt->data, pkt->size); + gxf_write_padding(pb, padding); + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!(gxf->flt_entries_nb % 500)) { + gxf->flt_entries = av_realloc_f(gxf->flt_entries, + sizeof(*gxf->flt_entries), + gxf->flt_entries_nb+500); + if (!gxf->flt_entries) { + av_log(s, AV_LOG_ERROR, "could not reallocate flt entries\n"); + return -1; + } + } + gxf->flt_entries[gxf->flt_entries_nb++] = packet_start_offset; + gxf->nb_fields += 2; // count fields + } + + updatePacketSize(pb, pos); + + gxf->packet_count++; + if (gxf->packet_count == 100) { + gxf_write_map_packet(s, 0); + gxf->packet_count = 0; + } + + return 0; +} + +static int gxf_compare_field_nb(AVFormatContext *s, AVPacket *next, AVPacket *cur) +{ + GXFContext *gxf = s->priv_data; + AVPacket *pkt[2] = { cur, next }; + int i, field_nb[2]; + GXFStreamContext *sc[2]; + + for (i = 0; i < 2; i++) { + AVStream *st = s->streams[pkt[i]->stream_index]; + sc[i] = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + field_nb[i] = av_rescale_rnd(pkt[i]->dts, gxf->time_base.den, + (int64_t)48000*gxf->time_base.num, AV_ROUND_UP); + field_nb[i] &= ~1; // compare against even field number because audio must be before video + } else + field_nb[i] = pkt[i]->dts; // dts are field based + } + + return field_nb[1] > field_nb[0] || + (field_nb[1] == field_nb[0] && sc[1]->order > sc[0]->order); +} + +static int gxf_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + if (pkt && s->streams[pkt->stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + pkt->duration = 2; // enforce 2 fields + return ff_audio_rechunk_interleave(s, out, pkt, flush, + ff_interleave_packet_per_dts, gxf_compare_field_nb); +} + +AVOutputFormat ff_gxf_muxer = { + .name = "gxf", + .long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), + .extensions = "gxf", + .priv_data_size = sizeof(GXFContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = gxf_write_header, + .write_packet = gxf_write_packet, + .write_trailer = gxf_write_trailer, + .interleave_packet = gxf_interleave_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxfenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/gxfenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/gxfenc.o libavformat/gxfenc.o: libavformat/gxfenc.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/intfloat.h \ + libavutil/opt.h libavutil/samplefmt.h libavutil/mathematics.h \ + libavutil/timecode.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/gxf.h libavformat/audiointerleave.h libavutil/fifo.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/gxfenc.o Binary file ffmpeg/libavformat/gxfenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h261dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h261dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,65 @@ +/* + * RAW H.261 video demuxer + * Copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "rawdec.h" + +static int h261_probe(AVProbeData *p) +{ + uint32_t code= -1; + int i; + int valid_psc=0; + int invalid_psc=0; + int next_gn=0; + int src_fmt=0; + GetBitContext gb; + + init_get_bits(&gb, p->buf, p->buf_size*8); + + for(i=0; ibuf_size*8; i++){ + if ((code & 0x01ff0000) || !(code & 0xff00)) { + code = (code<<8) + get_bits(&gb, 8); + i += 7; + } else + code = (code<<1) + get_bits1(&gb); + if ((code & 0xffff0000) == 0x10000) { + int gn= (code>>12)&0xf; + if(!gn) + src_fmt= code&8; + if(gn != next_gn) invalid_psc++; + else valid_psc++; + + if(src_fmt){ // CIF + next_gn= (gn+1 )%13; + }else{ //QCIF + next_gn= (gn+1+!!gn)% 7; + } + } + } + if(valid_psc > 2*invalid_psc + 6){ + return 50; + }else if(valid_psc > 2*invalid_psc + 2) + return 25; + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(h261, "raw H.261", h261_probe, "h261", AV_CODEC_ID_H261) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h261dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h261dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/h261dec.o libavformat/h261dec.o: libavformat/h261dec.c \ + libavcodec/get_bits.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/rawdec.h libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h261dec.o Binary file ffmpeg/libavformat/h261dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h263dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h263dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,65 @@ +/* + * RAW H.263 video demuxer + * Copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +static int h263_probe(AVProbeData *p) +{ + uint64_t code= -1; + int i; + int valid_psc=0; + int invalid_psc=0; + int res_change=0; + int src_fmt, last_src_fmt=-1; + int last_gn=0; + + for(i=0; ibuf_size; i++){ + code = (code<<8) + p->buf[i]; + if ((code & 0xfffffc0000) == 0x800000) { + src_fmt= (code>>2)&3; + if( src_fmt != last_src_fmt + && last_src_fmt>0 && last_src_fmt<6 + && src_fmt<6) + res_change++; + + if((code&0x300)==0x200 && src_fmt){ + valid_psc++; + last_gn=0; + }else + invalid_psc++; + last_src_fmt= src_fmt; + } else if((code & 0xffff800000) == 0x800000) { + int gn= (code>>(23-5)) & 0x1F; + if(gn 2*invalid_psc + 2*res_change + 3){ + return 50; + }else if(valid_psc > 2*invalid_psc) + return 25; + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(h263, "raw H.263", h263_probe, NULL, AV_CODEC_ID_H263) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h263dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h263dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/h263dec.o libavformat/h263dec.o: libavformat/h263dec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h263dec.o Binary file ffmpeg/libavformat/h263dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h264dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h264dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,70 @@ +/* + * RAW H.264 video demuxer + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +static int h264_probe(AVProbeData *p) +{ + uint32_t code= -1; + int sps=0, pps=0, idr=0, res=0, sli=0; + int i; + + for(i=0; ibuf_size; i++){ + code = (code<<8) + p->buf[i]; + if ((code & 0xffffff00) == 0x100) { + int ref_idc= (code>>5)&3; + int type = code & 0x1F; + static const int8_t ref_zero[32]={ + 2, 0, 0, 0, 0,-1, 1,-1, + -1, 1, 1, 1, 1,-1, 2, 2, + 2, 2, 2, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2 + }; + + if(code & 0x80) //forbidden bit + return 0; + + if(ref_zero[type] == 1 && ref_idc) + return 0; + if(ref_zero[type] ==-1 && !ref_idc) + return 0; + if(ref_zero[type] == 2) + res++; + + switch(type){ + case 1: sli++; break; + case 5: idr++; break; + case 7: + if (p->buf[i + 2] & 0x03) + return 0; + sps++; + break; + case 8: pps++; break; + } + } + } + if(sps && pps && (idr||sli>3) && res<(sps+pps+idr)) + return AVPROBE_SCORE_MAX/2+1; // +1 for .mpg + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(h264 , "raw H.264 video", h264_probe, "h26l,h264,264", AV_CODEC_ID_H264) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h264dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/h264dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/h264dec.o libavformat/h264dec.o: libavformat/h264dec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/h264dec.o Binary file ffmpeg/libavformat/h264dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hls.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hls.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,828 @@ +/* + * Apple HTTP Live Streaming demuxer + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Apple HTTP Live Streaming demuxer + * http://tools.ietf.org/html/draft-pantos-http-live-streaming + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/dict.h" +#include "libavutil/time.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "url.h" + +#define INITIAL_BUFFER_SIZE 32768 + +/* + * An apple http stream consists of a playlist with media segment files, + * played sequentially. There may be several playlists with the same + * video content, in different bandwidth variants, that are played in + * parallel (preferably only one bandwidth variant at a time). In this case, + * the user supplied the url to a main playlist that only lists the variant + * playlists. + * + * If the main playlist doesn't point at any variants, we still create + * one anonymous toplevel variant for this, to maintain the structure. + */ + +enum KeyType { + KEY_NONE, + KEY_AES_128, +}; + +struct segment { + int duration; + char url[MAX_URL_SIZE]; + char key[MAX_URL_SIZE]; + enum KeyType key_type; + uint8_t iv[16]; +}; + +/* + * Each variant has its own demuxer. If it currently is active, + * it has an open AVIOContext too, and potentially an AVPacket + * containing the next packet from this stream. + */ +struct variant { + int bandwidth; + char url[MAX_URL_SIZE]; + AVIOContext pb; + uint8_t* read_buffer; + URLContext *input; + AVFormatContext *parent; + int index; + AVFormatContext *ctx; + AVPacket pkt; + int stream_offset; + + int finished; + int target_duration; + int start_seq_no; + int n_segments; + struct segment **segments; + int needed, cur_needed; + int cur_seq_no; + int64_t last_load_time; + + char key_url[MAX_URL_SIZE]; + uint8_t key[16]; +}; + +typedef struct HLSContext { + int n_variants; + struct variant **variants; + int cur_seq_no; + int end_of_segment; + int first_packet; + int64_t first_timestamp; + int64_t seek_timestamp; + int seek_flags; + AVIOInterruptCB *interrupt_callback; + char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context + char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context +} HLSContext; + +static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) +{ + int len = ff_get_line(s, buf, maxlen); + while (len > 0 && av_isspace(buf[len - 1])) + buf[--len] = '\0'; + return len; +} + +static void free_segment_list(struct variant *var) +{ + int i; + for (i = 0; i < var->n_segments; i++) + av_free(var->segments[i]); + av_freep(&var->segments); + var->n_segments = 0; +} + +static void free_variant_list(HLSContext *c) +{ + int i; + for (i = 0; i < c->n_variants; i++) { + struct variant *var = c->variants[i]; + free_segment_list(var); + av_free_packet(&var->pkt); + av_free(var->pb.buffer); + if (var->input) + ffurl_close(var->input); + if (var->ctx) { + var->ctx->pb = NULL; + avformat_close_input(&var->ctx); + } + av_free(var); + } + av_freep(&c->variants); + av_freep(&c->cookies); + av_freep(&c->user_agent); + c->n_variants = 0; +} + +/* + * Used to reset a statically allocated AVPacket to a clean slate, + * containing no data. + */ +static void reset_packet(AVPacket *pkt) +{ + av_init_packet(pkt); + pkt->data = NULL; +} + +static struct variant *new_variant(HLSContext *c, int bandwidth, + const char *url, const char *base) +{ + struct variant *var = av_mallocz(sizeof(struct variant)); + if (!var) + return NULL; + reset_packet(&var->pkt); + var->bandwidth = bandwidth; + ff_make_absolute_url(var->url, sizeof(var->url), base, url); + dynarray_add(&c->variants, &c->n_variants, var); + return var; +} + +struct variant_info { + char bandwidth[20]; +}; + +static void handle_variant_args(struct variant_info *info, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "BANDWIDTH=", key_len)) { + *dest = info->bandwidth; + *dest_len = sizeof(info->bandwidth); + } +} + +struct key_info { + char uri[MAX_URL_SIZE]; + char method[10]; + char iv[35]; +}; + +static void handle_key_args(struct key_info *info, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "METHOD=", key_len)) { + *dest = info->method; + *dest_len = sizeof(info->method); + } else if (!strncmp(key, "URI=", key_len)) { + *dest = info->uri; + *dest_len = sizeof(info->uri); + } else if (!strncmp(key, "IV=", key_len)) { + *dest = info->iv; + *dest_len = sizeof(info->iv); + } +} + +static int parse_playlist(HLSContext *c, const char *url, + struct variant *var, AVIOContext *in) +{ + int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0; + enum KeyType key_type = KEY_NONE; + uint8_t iv[16] = ""; + int has_iv = 0; + char key[MAX_URL_SIZE] = ""; + char line[1024]; + const char *ptr; + int close_in = 0; + + if (!in) { + AVDictionary *opts = NULL; + close_in = 1; + /* Some HLS servers dont like being sent the range header */ + av_dict_set(&opts, "seekable", "0", 0); + + // broker prior HTTP options that should be consistent across requests + av_dict_set(&opts, "user-agent", c->user_agent, 0); + av_dict_set(&opts, "cookies", c->cookies, 0); + + ret = avio_open2(&in, url, AVIO_FLAG_READ, + c->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) + return ret; + } + + read_chomp_line(in, line, sizeof(line)); + if (strcmp(line, "#EXTM3U")) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (var) { + free_segment_list(var); + var->finished = 0; + } + while (!url_feof(in)) { + read_chomp_line(in, line, sizeof(line)); + if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { + struct variant_info info = {{0}}; + is_variant = 1; + ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, + &info); + bandwidth = atoi(info.bandwidth); + } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) { + struct key_info info = {{0}}; + ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args, + &info); + key_type = KEY_NONE; + has_iv = 0; + if (!strcmp(info.method, "AES-128")) + key_type = KEY_AES_128; + if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) { + ff_hex_to_data(iv, info.iv + 2); + has_iv = 1; + } + av_strlcpy(key, info.uri, sizeof(key)); + } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { + if (!var) { + var = new_variant(c, 0, url, NULL); + if (!var) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + var->target_duration = atoi(ptr); + } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { + if (!var) { + var = new_variant(c, 0, url, NULL); + if (!var) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + var->start_seq_no = atoi(ptr); + } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { + if (var) + var->finished = 1; + } else if (av_strstart(line, "#EXTINF:", &ptr)) { + is_segment = 1; + duration = atoi(ptr); + } else if (av_strstart(line, "#", NULL)) { + continue; + } else if (line[0]) { + if (is_variant) { + if (!new_variant(c, bandwidth, line, url)) { + ret = AVERROR(ENOMEM); + goto fail; + } + is_variant = 0; + bandwidth = 0; + } + if (is_segment) { + struct segment *seg; + if (!var) { + var = new_variant(c, 0, url, NULL); + if (!var) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + seg = av_malloc(sizeof(struct segment)); + if (!seg) { + ret = AVERROR(ENOMEM); + goto fail; + } + seg->duration = duration; + seg->key_type = key_type; + if (has_iv) { + memcpy(seg->iv, iv, sizeof(iv)); + } else { + int seq = var->start_seq_no + var->n_segments; + memset(seg->iv, 0, sizeof(seg->iv)); + AV_WB32(seg->iv + 12, seq); + } + ff_make_absolute_url(seg->key, sizeof(seg->key), url, key); + ff_make_absolute_url(seg->url, sizeof(seg->url), url, line); + dynarray_add(&var->segments, &var->n_segments, seg); + is_segment = 0; + } + } + } + if (var) + var->last_load_time = av_gettime(); + +fail: + if (close_in) + avio_close(in); + return ret; +} + +static int open_input(HLSContext *c, struct variant *var) +{ + AVDictionary *opts = NULL; + int ret; + struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no]; + + // broker prior HTTP options that should be consistent across requests + av_dict_set(&opts, "user-agent", c->user_agent, 0); + av_dict_set(&opts, "cookies", c->cookies, 0); + av_dict_set(&opts, "seekable", "0", 0); + + if (seg->key_type == KEY_NONE) { + ret = ffurl_open(&var->input, seg->url, AVIO_FLAG_READ, + &var->parent->interrupt_callback, &opts); + goto cleanup; + } else if (seg->key_type == KEY_AES_128) { + char iv[33], key[33], url[MAX_URL_SIZE]; + if (strcmp(seg->key, var->key_url)) { + URLContext *uc; + if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ, + &var->parent->interrupt_callback, &opts) == 0) { + if (ffurl_read_complete(uc, var->key, sizeof(var->key)) + != sizeof(var->key)) { + av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n", + seg->key); + } + ffurl_close(uc); + } else { + av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n", + seg->key); + } + av_strlcpy(var->key_url, seg->key, sizeof(var->key_url)); + } + ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0); + ff_data_to_hex(key, var->key, sizeof(var->key), 0); + iv[32] = key[32] = '\0'; + if (strstr(seg->url, "://")) + snprintf(url, sizeof(url), "crypto+%s", seg->url); + else + snprintf(url, sizeof(url), "crypto:%s", seg->url); + if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ, + &var->parent->interrupt_callback)) < 0) + goto cleanup; + av_opt_set(var->input->priv_data, "key", key, 0); + av_opt_set(var->input->priv_data, "iv", iv, 0); + /* Need to repopulate options */ + av_dict_free(&opts); + av_dict_set(&opts, "seekable", "0", 0); + if ((ret = ffurl_connect(var->input, &opts)) < 0) { + ffurl_close(var->input); + var->input = NULL; + goto cleanup; + } + ret = 0; + } + else + ret = AVERROR(ENOSYS); + +cleanup: + av_dict_free(&opts); + return ret; +} + +static int read_data(void *opaque, uint8_t *buf, int buf_size) +{ + struct variant *v = opaque; + HLSContext *c = v->parent->priv_data; + int ret, i; + +restart: + if (!v->input) { + /* If this is a live stream and the reload interval has elapsed since + * the last playlist reload, reload the variant playlists now. */ + int64_t reload_interval = v->n_segments > 0 ? + v->segments[v->n_segments - 1]->duration : + v->target_duration; + reload_interval *= 1000000; + +reload: + if (!v->finished && + av_gettime() - v->last_load_time >= reload_interval) { + if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) + return ret; + /* If we need to reload the playlist again below (if + * there's still no more segments), switch to a reload + * interval of half the target duration. */ + reload_interval = v->target_duration * 500000LL; + } + if (v->cur_seq_no < v->start_seq_no) { + av_log(NULL, AV_LOG_WARNING, + "skipping %d segments ahead, expired from playlists\n", + v->start_seq_no - v->cur_seq_no); + v->cur_seq_no = v->start_seq_no; + } + if (v->cur_seq_no >= v->start_seq_no + v->n_segments) { + if (v->finished) + return AVERROR_EOF; + while (av_gettime() - v->last_load_time < reload_interval) { + if (ff_check_interrupt(c->interrupt_callback)) + return AVERROR_EXIT; + av_usleep(100*1000); + } + /* Enough time has elapsed since the last reload */ + goto reload; + } + + ret = open_input(c, v); + if (ret < 0) + return ret; + } + ret = ffurl_read(v->input, buf, buf_size); + if (ret > 0) + return ret; + ffurl_close(v->input); + v->input = NULL; + v->cur_seq_no++; + + c->end_of_segment = 1; + c->cur_seq_no = v->cur_seq_no; + + if (v->ctx && v->ctx->nb_streams && v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) { + v->needed = 0; + for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams; + i++) { + if (v->parent->streams[i]->discard < AVDISCARD_ALL) + v->needed = 1; + } + } + if (!v->needed) { + av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n", + v->index); + return AVERROR_EOF; + } + goto restart; +} + +static int hls_read_header(AVFormatContext *s) +{ + URLContext *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb->opaque; + HLSContext *c = s->priv_data; + int ret = 0, i, j, stream_offset = 0; + + c->interrupt_callback = &s->interrupt_callback; + + // if the URL context is good, read important options we must broker later + if (u && u->prot->priv_data_class) { + // get the previous user agent & set back to null if string size is zero + av_freep(&c->user_agent); + av_opt_get(u->priv_data, "user-agent", 0, (uint8_t**)&(c->user_agent)); + if (c->user_agent && !strlen(c->user_agent)) + av_freep(&c->user_agent); + + // get the previous cookies & set back to null if string size is zero + av_freep(&c->cookies); + av_opt_get(u->priv_data, "cookies", 0, (uint8_t**)&(c->cookies)); + if (c->cookies && !strlen(c->cookies)) + av_freep(&c->cookies); + } + + if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0) + goto fail; + + if (c->n_variants == 0) { + av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); + ret = AVERROR_EOF; + goto fail; + } + /* If the playlist only contained variants, parse each individual + * variant playlist. */ + if (c->n_variants > 1 || c->variants[0]->n_segments == 0) { + for (i = 0; i < c->n_variants; i++) { + struct variant *v = c->variants[i]; + if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) + goto fail; + } + } + + if (c->variants[0]->n_segments == 0) { + av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); + ret = AVERROR_EOF; + goto fail; + } + + /* If this isn't a live stream, calculate the total duration of the + * stream. */ + if (c->variants[0]->finished) { + int64_t duration = 0; + for (i = 0; i < c->variants[0]->n_segments; i++) + duration += c->variants[0]->segments[i]->duration; + s->duration = duration * AV_TIME_BASE; + } + + /* Open the demuxer for each variant */ + for (i = 0; i < c->n_variants; i++) { + struct variant *v = c->variants[i]; + AVInputFormat *in_fmt = NULL; + char bitrate_str[20]; + AVProgram *program = NULL; + if (v->n_segments == 0) + continue; + + if (!(v->ctx = avformat_alloc_context())) { + ret = AVERROR(ENOMEM); + goto fail; + } + + v->index = i; + v->needed = 1; + v->parent = s; + + /* If this is a live stream with more than 3 segments, start at the + * third last segment. */ + v->cur_seq_no = v->start_seq_no; + if (!v->finished && v->n_segments > 3) + v->cur_seq_no = v->start_seq_no + v->n_segments - 3; + + v->read_buffer = av_malloc(INITIAL_BUFFER_SIZE); + ffio_init_context(&v->pb, v->read_buffer, INITIAL_BUFFER_SIZE, 0, v, + read_data, NULL, NULL); + v->pb.seekable = 0; + ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url, + NULL, 0, 0); + if (ret < 0) { + /* Free the ctx - it isn't initialized properly at this point, + * so avformat_close_input shouldn't be called. If + * avformat_open_input fails below, it frees and zeros the + * context, so it doesn't need any special treatment like this. */ + av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", v->segments[0]->url); + avformat_free_context(v->ctx); + v->ctx = NULL; + goto fail; + } + v->ctx->pb = &v->pb; + ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL); + if (ret < 0) + goto fail; + + v->stream_offset = stream_offset; + v->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER; + ret = avformat_find_stream_info(v->ctx, NULL); + if (ret < 0) + goto fail; + snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth); + + /* Create new AVprogram for variant i */ + program = av_new_program(s, i); + if (!program) + goto fail; + av_dict_set(&program->metadata, "variant_bitrate", bitrate_str, 0); + + /* Create new AVStreams for each stream in this variant */ + for (j = 0; j < v->ctx->nb_streams; j++) { + AVStream *st = avformat_new_stream(s, NULL); + AVStream *ist = v->ctx->streams[j]; + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + ff_program_add_stream_index(s, i, stream_offset + j); + st->id = i; + avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den); + avcodec_copy_context(st->codec, v->ctx->streams[j]->codec); + if (v->bandwidth) + av_dict_set(&st->metadata, "variant_bitrate", bitrate_str, + 0); + } + stream_offset += v->ctx->nb_streams; + } + + c->first_packet = 1; + c->first_timestamp = AV_NOPTS_VALUE; + c->seek_timestamp = AV_NOPTS_VALUE; + + return 0; +fail: + free_variant_list(c); + return ret; +} + +static int recheck_discard_flags(AVFormatContext *s, int first) +{ + HLSContext *c = s->priv_data; + int i, changed = 0; + + /* Check if any new streams are needed */ + for (i = 0; i < c->n_variants; i++) + c->variants[i]->cur_needed = 0; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + struct variant *var = c->variants[s->streams[i]->id]; + if (st->discard < AVDISCARD_ALL) + var->cur_needed = 1; + } + for (i = 0; i < c->n_variants; i++) { + struct variant *v = c->variants[i]; + if (v->cur_needed && !v->needed) { + v->needed = 1; + changed = 1; + v->cur_seq_no = c->cur_seq_no; + v->pb.eof_reached = 0; + av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i); + } else if (first && !v->cur_needed && v->needed) { + if (v->input) + ffurl_close(v->input); + v->input = NULL; + v->needed = 0; + changed = 1; + av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i); + } + } + return changed; +} + +static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + HLSContext *c = s->priv_data; + int ret, i, minvariant = -1; + + if (c->first_packet) { + recheck_discard_flags(s, 1); + c->first_packet = 0; + } + +start: + c->end_of_segment = 0; + for (i = 0; i < c->n_variants; i++) { + struct variant *var = c->variants[i]; + /* Make sure we've got one buffered packet from each open variant + * stream */ + if (var->needed && !var->pkt.data) { + while (1) { + int64_t ts_diff; + AVStream *st; + ret = av_read_frame(var->ctx, &var->pkt); + if (ret < 0) { + if (!url_feof(&var->pb) && ret != AVERROR_EOF) + return ret; + reset_packet(&var->pkt); + break; + } else { + if (c->first_timestamp == AV_NOPTS_VALUE) + c->first_timestamp = var->pkt.dts; + } + + if (c->seek_timestamp == AV_NOPTS_VALUE) + break; + + if (var->pkt.dts == AV_NOPTS_VALUE) { + c->seek_timestamp = AV_NOPTS_VALUE; + break; + } + + st = var->ctx->streams[var->pkt.stream_index]; + ts_diff = av_rescale_rnd(var->pkt.dts, AV_TIME_BASE, + st->time_base.den, AV_ROUND_DOWN) - + c->seek_timestamp; + if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY || + var->pkt.flags & AV_PKT_FLAG_KEY)) { + c->seek_timestamp = AV_NOPTS_VALUE; + break; + } + } + } + /* Check if this stream has the packet with the lowest dts */ + if (var->pkt.data) { + if(minvariant < 0) { + minvariant = i; + } else { + struct variant *minvar = c->variants[minvariant]; + int64_t dts = var->pkt.dts; + int64_t mindts = minvar->pkt.dts; + AVStream *st = var->ctx->streams[ var->pkt.stream_index]; + AVStream *minst= minvar->ctx->streams[minvar->pkt.stream_index]; + + if( st->start_time != AV_NOPTS_VALUE) dts -= st->start_time; + if(minst->start_time != AV_NOPTS_VALUE) mindts -= minst->start_time; + + if (av_compare_ts(dts, st->time_base, mindts, minst->time_base) < 0) + minvariant = i; + } + } + } + if (c->end_of_segment) { + if (recheck_discard_flags(s, 0)) + goto start; + } + /* If we got a packet, return it */ + if (minvariant >= 0) { + *pkt = c->variants[minvariant]->pkt; + pkt->stream_index += c->variants[minvariant]->stream_offset; + reset_packet(&c->variants[minvariant]->pkt); + return 0; + } + return AVERROR_EOF; +} + +static int hls_close(AVFormatContext *s) +{ + HLSContext *c = s->priv_data; + + free_variant_list(c); + return 0; +} + +static int hls_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + HLSContext *c = s->priv_data; + int i, j, ret; + + if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished) + return AVERROR(ENOSYS); + + c->seek_flags = flags; + c->seek_timestamp = stream_index < 0 ? timestamp : + av_rescale_rnd(timestamp, AV_TIME_BASE, + s->streams[stream_index]->time_base.den, + flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ? + s->streams[stream_index]->time_base.den : + AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + if (s->duration < c->seek_timestamp) { + c->seek_timestamp = AV_NOPTS_VALUE; + return AVERROR(EIO); + } + + ret = AVERROR(EIO); + for (i = 0; i < c->n_variants; i++) { + /* Reset reading */ + struct variant *var = c->variants[i]; + int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? 0 : + av_rescale_rnd(c->first_timestamp, 1, stream_index >= 0 ? + s->streams[stream_index]->time_base.den : + AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? + AV_ROUND_DOWN : AV_ROUND_UP); + if (var->input) { + ffurl_close(var->input); + var->input = NULL; + } + av_free_packet(&var->pkt); + reset_packet(&var->pkt); + var->pb.eof_reached = 0; + /* Clear any buffered data */ + var->pb.buf_end = var->pb.buf_ptr = var->pb.buffer; + /* Reset the pos, to let the mpegts demuxer know we've seeked. */ + var->pb.pos = 0; + + /* Locate the segment that contains the target timestamp */ + for (j = 0; j < var->n_segments; j++) { + if (timestamp >= pos && + timestamp < pos + var->segments[j]->duration) { + var->cur_seq_no = var->start_seq_no + j; + ret = 0; + break; + } + pos += var->segments[j]->duration; + } + if (ret) + c->seek_timestamp = AV_NOPTS_VALUE; + } + return ret; +} + +static int hls_probe(AVProbeData *p) +{ + /* Require #EXTM3U at the start, and either one of the ones below + * somewhere for a proper match. */ + if (strncmp(p->buf, "#EXTM3U", 7)) + return 0; + if (strstr(p->buf, "#EXT-X-STREAM-INF:") || + strstr(p->buf, "#EXT-X-TARGETDURATION:") || + strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) + return AVPROBE_SCORE_MAX; + return 0; +} + +AVInputFormat ff_hls_demuxer = { + .name = "hls,applehttp", + .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), + .priv_data_size = sizeof(HLSContext), + .read_probe = hls_probe, + .read_header = hls_read_header, + .read_packet = hls_read_packet, + .read_close = hls_close, + .read_seek = hls_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hls.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hls.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/hls.o libavformat/hls.o: libavformat/hls.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavutil/dict.h libavutil/time.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/avio_internal.h \ + libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hls.o Binary file ffmpeg/libavformat/hls.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hlsenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,348 @@ +/* + * Apple HTTP Live Streaming segmenter + * Copyright (c) 2012, Luca Barbato + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/mathematics.h" +#include "libavutil/parseutils.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/log.h" + +#include "avformat.h" +#include "internal.h" + +typedef struct ListEntry { + char name[1024]; + int duration; + struct ListEntry *next; +} ListEntry; + +typedef struct HLSContext { + const AVClass *class; // Class for private options. + unsigned number; + int64_t sequence; + AVOutputFormat *oformat; + AVFormatContext *avf; + float time; // Set by a private option. + int size; // Set by a private option. + int wrap; // Set by a private option. + int64_t recording_time; + int has_video; + int64_t start_pts; + int64_t end_pts; + int64_t duration; ///< last segment duration computed so far, in seconds + int nb_entries; + ListEntry *list; + ListEntry *end_list; + char *basename; + AVIOContext *pb; +} HLSContext; + +static int hls_mux_init(AVFormatContext *s) +{ + HLSContext *hls = s->priv_data; + AVFormatContext *oc; + int i; + + hls->avf = oc = avformat_alloc_context(); + if (!oc) + return AVERROR(ENOMEM); + + oc->oformat = hls->oformat; + oc->interrupt_callback = s->interrupt_callback; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st; + if (!(st = avformat_new_stream(oc, NULL))) + return AVERROR(ENOMEM); + avcodec_copy_context(st->codec, s->streams[i]->codec); + st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; + } + + return 0; +} + +static int append_entry(HLSContext *hls, uint64_t duration) +{ + ListEntry *en = av_malloc(sizeof(*en)); + + if (!en) + return AVERROR(ENOMEM); + + av_strlcpy(en->name, av_basename(hls->avf->filename), sizeof(en->name)); + + en->duration = duration; + en->next = NULL; + + if (!hls->list) + hls->list = en; + else + hls->end_list->next = en; + + hls->end_list = en; + + if (hls->nb_entries >= hls->size) { + en = hls->list; + hls->list = en->next; + av_free(en); + } else + hls->nb_entries++; + + hls->sequence++; + + return 0; +} + +static void free_entries(HLSContext *hls) +{ + ListEntry *p = hls->list, *en; + + while(p) { + en = p; + p = p->next; + av_free(en); + } +} + +static int hls_window(AVFormatContext *s, int last) +{ + HLSContext *hls = s->priv_data; + ListEntry *en; + int target_duration = 0; + int ret = 0; + + if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE, + &s->interrupt_callback, NULL)) < 0) + goto fail; + + for (en = hls->list; en; en = en->next) { + if (target_duration < en->duration) + target_duration = en->duration; + } + + avio_printf(hls->pb, "#EXTM3U\n"); + avio_printf(hls->pb, "#EXT-X-VERSION:3\n"); + avio_printf(hls->pb, "#EXT-X-TARGETDURATION:%d\n", target_duration); + avio_printf(hls->pb, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", + FFMAX(0, hls->sequence - hls->size)); + + for (en = hls->list; en; en = en->next) { + avio_printf(hls->pb, "#EXTINF:%d,\n", en->duration); + avio_printf(hls->pb, "%s\n", en->name); + } + + if (last) + avio_printf(hls->pb, "#EXT-X-ENDLIST\n"); + +fail: + avio_closep(&hls->pb); + return ret; +} + +static int hls_start(AVFormatContext *s) +{ + HLSContext *c = s->priv_data; + AVFormatContext *oc = c->avf; + int err = 0; + + if (c->wrap) + c->number %= c->wrap; + + if (av_get_frame_filename(oc->filename, sizeof(oc->filename), + c->basename, c->number++) < 0) { + av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename); + return AVERROR(EINVAL); + } + + if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, + &s->interrupt_callback, NULL)) < 0) + return err; + + if (oc->oformat->priv_class && oc->priv_data) + av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); + + return 0; +} + +static int hls_write_header(AVFormatContext *s) +{ + HLSContext *hls = s->priv_data; + int ret, i; + char *p; + const char *pattern = "%d.ts"; + int basename_size = strlen(s->filename) + strlen(pattern) + 1; + + hls->number = 0; + + hls->recording_time = hls->time * AV_TIME_BASE; + hls->start_pts = AV_NOPTS_VALUE; + + for (i = 0; i < s->nb_streams; i++) + hls->has_video += + s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO; + + if (hls->has_video > 1) + av_log(s, AV_LOG_WARNING, + "More than a single video stream present, " + "expect issues decoding it.\n"); + + hls->oformat = av_guess_format("mpegts", NULL, NULL); + + if (!hls->oformat) { + ret = AVERROR_MUXER_NOT_FOUND; + goto fail; + } + + hls->basename = av_malloc(basename_size); + + if (!hls->basename) { + ret = AVERROR(ENOMEM); + goto fail; + } + + strcpy(hls->basename, s->filename); + + p = strrchr(hls->basename, '.'); + + if (p) + *p = '\0'; + + av_strlcat(hls->basename, pattern, basename_size); + + if ((ret = hls_mux_init(s)) < 0) + goto fail; + + if ((ret = hls_start(s)) < 0) + goto fail; + + if ((ret = avformat_write_header(hls->avf, NULL)) < 0) + return ret; + + +fail: + if (ret) { + av_free(hls->basename); + if (hls->avf) + avformat_free_context(hls->avf); + } + return ret; +} + +static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + HLSContext *hls = s->priv_data; + AVFormatContext *oc = hls->avf; + AVStream *st = s->streams[pkt->stream_index]; + int64_t end_pts = hls->recording_time * hls->number; + int ret, is_ref_pkt = 0; + + if (hls->start_pts == AV_NOPTS_VALUE) { + hls->start_pts = pkt->pts; + hls->end_pts = pkt->pts; + } + + if ((hls->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) && + pkt->pts != AV_NOPTS_VALUE) { + is_ref_pkt = 1; + hls->duration = av_rescale(pkt->pts - hls->end_pts, + st->time_base.num, st->time_base.den); + } + + if (is_ref_pkt && + av_compare_ts(pkt->pts - hls->start_pts, st->time_base, + end_pts, AV_TIME_BASE_Q) >= 0 && + pkt->flags & AV_PKT_FLAG_KEY) { + + ret = append_entry(hls, hls->duration); + if (ret) + return ret; + + hls->end_pts = pkt->pts; + hls->duration = 0; + + av_write_frame(oc, NULL); /* Flush any buffered data */ + avio_close(oc->pb); + + ret = hls_start(s); + + if (ret) + return ret; + + oc = hls->avf; + + if ((ret = hls_window(s, 0)) < 0) + return ret; + } + + ret = ff_write_chained(oc, pkt->stream_index, pkt, s); + + return ret; +} + +static int hls_write_trailer(struct AVFormatContext *s) +{ + HLSContext *hls = s->priv_data; + AVFormatContext *oc = hls->avf; + + av_write_trailer(oc); + avio_closep(&oc->pb); + avformat_free_context(oc); + av_free(hls->basename); + append_entry(hls, hls->duration); + hls_window(s, 1); + + free_entries(hls); + avio_close(hls->pb); + return 0; +} + +#define OFFSET(x) offsetof(HLSContext, x) +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + {"start_number", "set first number in the sequence", OFFSET(sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, + {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, + {"hls_list_size", "set maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, + {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, + { NULL }, +}; + +static const AVClass hls_class = { + .class_name = "hls muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +AVOutputFormat ff_hls_muxer = { + .name = "hls", + .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), + .extensions = "m3u8", + .priv_data_size = sizeof(HLSContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, + .write_header = hls_write_header, + .write_packet = hls_write_packet, + .write_trailer = hls_write_trailer, + .priv_class = &hls_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hlsenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/hlsenc.o libavformat/hlsenc.o: libavformat/hlsenc.c \ + libavutil/mathematics.h libavutil/attributes.h libavutil/rational.h \ + libavutil/intfloat.h libavutil/parseutils.h libavutil/avstring.h \ + libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/log.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsenc.o Binary file ffmpeg/libavformat/hlsenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsproto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hlsproto.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,323 @@ +/* + * Apple HTTP Live Streaming Protocol Handler + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Apple HTTP Live Streaming Protocol Handler + * http://tools.ietf.org/html/draft-pantos-http-live-streaming + */ + +#include "libavutil/avstring.h" +#include "libavutil/time.h" +#include "avformat.h" +#include "internal.h" +#include "url.h" +#include "version.h" + +/* + * An apple http stream consists of a playlist with media segment files, + * played sequentially. There may be several playlists with the same + * video content, in different bandwidth variants, that are played in + * parallel (preferably only one bandwidth variant at a time). In this case, + * the user supplied the url to a main playlist that only lists the variant + * playlists. + * + * If the main playlist doesn't point at any variants, we still create + * one anonymous toplevel variant for this, to maintain the structure. + */ + +struct segment { + int duration; + char url[MAX_URL_SIZE]; +}; + +struct variant { + int bandwidth; + char url[MAX_URL_SIZE]; +}; + +typedef struct HLSContext { + char playlisturl[MAX_URL_SIZE]; + int target_duration; + int start_seq_no; + int finished; + int n_segments; + struct segment **segments; + int n_variants; + struct variant **variants; + int cur_seq_no; + URLContext *seg_hd; + int64_t last_load_time; +} HLSContext; + +static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) +{ + int len = ff_get_line(s, buf, maxlen); + while (len > 0 && av_isspace(buf[len - 1])) + buf[--len] = '\0'; + return len; +} + +static void free_segment_list(HLSContext *s) +{ + int i; + for (i = 0; i < s->n_segments; i++) + av_free(s->segments[i]); + av_freep(&s->segments); + s->n_segments = 0; +} + +static void free_variant_list(HLSContext *s) +{ + int i; + for (i = 0; i < s->n_variants; i++) + av_free(s->variants[i]); + av_freep(&s->variants); + s->n_variants = 0; +} + +struct variant_info { + char bandwidth[20]; +}; + +static void handle_variant_args(struct variant_info *info, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "BANDWIDTH=", key_len)) { + *dest = info->bandwidth; + *dest_len = sizeof(info->bandwidth); + } +} + +static int parse_playlist(URLContext *h, const char *url) +{ + HLSContext *s = h->priv_data; + AVIOContext *in; + int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0; + char line[1024]; + const char *ptr; + + if ((ret = avio_open2(&in, url, AVIO_FLAG_READ, + &h->interrupt_callback, NULL)) < 0) + return ret; + + read_chomp_line(in, line, sizeof(line)); + if (strcmp(line, "#EXTM3U")) + return AVERROR_INVALIDDATA; + + free_segment_list(s); + s->finished = 0; + while (!url_feof(in)) { + read_chomp_line(in, line, sizeof(line)); + if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { + struct variant_info info = {{0}}; + is_variant = 1; + ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, + &info); + bandwidth = atoi(info.bandwidth); + } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { + s->target_duration = atoi(ptr); + } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { + s->start_seq_no = atoi(ptr); + } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { + s->finished = 1; + } else if (av_strstart(line, "#EXTINF:", &ptr)) { + is_segment = 1; + duration = atoi(ptr); + } else if (av_strstart(line, "#", NULL)) { + continue; + } else if (line[0]) { + if (is_segment) { + struct segment *seg = av_malloc(sizeof(struct segment)); + if (!seg) { + ret = AVERROR(ENOMEM); + goto fail; + } + seg->duration = duration; + ff_make_absolute_url(seg->url, sizeof(seg->url), url, line); + dynarray_add(&s->segments, &s->n_segments, seg); + is_segment = 0; + } else if (is_variant) { + struct variant *var = av_malloc(sizeof(struct variant)); + if (!var) { + ret = AVERROR(ENOMEM); + goto fail; + } + var->bandwidth = bandwidth; + ff_make_absolute_url(var->url, sizeof(var->url), url, line); + dynarray_add(&s->variants, &s->n_variants, var); + is_variant = 0; + } + } + } + s->last_load_time = av_gettime(); + +fail: + avio_close(in); + return ret; +} + +static int hls_close(URLContext *h) +{ + HLSContext *s = h->priv_data; + + free_segment_list(s); + free_variant_list(s); + ffurl_close(s->seg_hd); + return 0; +} + +static int hls_open(URLContext *h, const char *uri, int flags) +{ + HLSContext *s = h->priv_data; + int ret, i; + const char *nested_url; + + if (flags & AVIO_FLAG_WRITE) + return AVERROR(ENOSYS); + + h->is_streamed = 1; + + if (av_strstart(uri, "hls+", &nested_url)) { + av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl)); + } else if (av_strstart(uri, "hls://", &nested_url)) { + av_log(h, AV_LOG_ERROR, + "No nested protocol specified. Specify e.g. hls+http://%s\n", + nested_url); + ret = AVERROR(EINVAL); + goto fail; + } else { + av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri); + ret = AVERROR(EINVAL); + goto fail; + } + av_log(h, AV_LOG_WARNING, + "Using the hls protocol is discouraged, please try using the " + "hls demuxer instead. The hls demuxer should be more complete " + "and work as well as the protocol implementation. (If not, " + "please report it.) To use the demuxer, simply use %s as url.\n", + s->playlisturl); + + if ((ret = parse_playlist(h, s->playlisturl)) < 0) + goto fail; + + if (s->n_segments == 0 && s->n_variants > 0) { + int max_bandwidth = 0, maxvar = -1; + for (i = 0; i < s->n_variants; i++) { + if (s->variants[i]->bandwidth > max_bandwidth || i == 0) { + max_bandwidth = s->variants[i]->bandwidth; + maxvar = i; + } + } + av_strlcpy(s->playlisturl, s->variants[maxvar]->url, + sizeof(s->playlisturl)); + if ((ret = parse_playlist(h, s->playlisturl)) < 0) + goto fail; + } + + if (s->n_segments == 0) { + av_log(h, AV_LOG_WARNING, "Empty playlist\n"); + ret = AVERROR(EIO); + goto fail; + } + s->cur_seq_no = s->start_seq_no; + if (!s->finished && s->n_segments >= 3) + s->cur_seq_no = s->start_seq_no + s->n_segments - 3; + + return 0; + +fail: + hls_close(h); + return ret; +} + +static int hls_read(URLContext *h, uint8_t *buf, int size) +{ + HLSContext *s = h->priv_data; + const char *url; + int ret; + int64_t reload_interval; + +start: + if (s->seg_hd) { + ret = ffurl_read(s->seg_hd, buf, size); + if (ret > 0) + return ret; + } + if (s->seg_hd) { + ffurl_close(s->seg_hd); + s->seg_hd = NULL; + s->cur_seq_no++; + } + reload_interval = s->n_segments > 0 ? + s->segments[s->n_segments - 1]->duration : + s->target_duration; + reload_interval *= 1000000; +retry: + if (!s->finished) { + int64_t now = av_gettime(); + if (now - s->last_load_time >= reload_interval) { + if ((ret = parse_playlist(h, s->playlisturl)) < 0) + return ret; + /* If we need to reload the playlist again below (if + * there's still no more segments), switch to a reload + * interval of half the target duration. */ + reload_interval = s->target_duration * 500000LL; + } + } + if (s->cur_seq_no < s->start_seq_no) { + av_log(h, AV_LOG_WARNING, + "skipping %d segments ahead, expired from playlist\n", + s->start_seq_no - s->cur_seq_no); + s->cur_seq_no = s->start_seq_no; + } + if (s->cur_seq_no - s->start_seq_no >= s->n_segments) { + if (s->finished) + return AVERROR_EOF; + while (av_gettime() - s->last_load_time < reload_interval) { + if (ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; + av_usleep(100*1000); + } + goto retry; + } + url = s->segments[s->cur_seq_no - s->start_seq_no]->url, + av_log(h, AV_LOG_DEBUG, "opening %s\n", url); + ret = ffurl_open(&s->seg_hd, url, AVIO_FLAG_READ, + &h->interrupt_callback, NULL); + if (ret < 0) { + if (ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; + av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url); + s->cur_seq_no++; + goto retry; + } + goto start; +} + +URLProtocol ff_hls_protocol = { + .name = "hls", + .url_open = hls_open, + .url_read = hls_read, + .url_close = hls_close, + .flags = URL_PROTOCOL_FLAG_NESTED_SCHEME, + .priv_data_size = sizeof(HLSContext), +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsproto.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/hlsproto.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/hlsproto.o libavformat/hlsproto.o: libavformat/hlsproto.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/time.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/url.h libavformat/version.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/hlsproto.o Binary file ffmpeg/libavformat/hlsproto.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/http.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,944 @@ +/* + * HTTP protocol for ffmpeg client + * Copyright (c) 2000, 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "avformat.h" +#include "internal.h" +#include "network.h" +#include "http.h" +#include "os_support.h" +#include "httpauth.h" +#include "url.h" +#include "libavutil/opt.h" + +/* XXX: POST protocol is not completely implemented because ffmpeg uses + only a subset of it. */ + +/* The IO buffer size is unrelated to the max URL size in itself, but needs + * to be large enough to fit the full request headers (including long + * path names). + */ +#define BUFFER_SIZE MAX_URL_SIZE +#define MAX_REDIRECTS 8 + +typedef struct { + const AVClass *class; + URLContext *hd; + unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end; + int line_count; + int http_code; + int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */ + char *content_type; + char *user_agent; + int64_t off, filesize; + char location[MAX_URL_SIZE]; + HTTPAuthState auth_state; + HTTPAuthState proxy_auth_state; + char *headers; + int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */ + int seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */ + int chunked_post; + int end_chunked_post; /**< A flag which indicates if the end of chunked encoding has been sent. */ + int end_header; /**< A flag which indicates we have finished to read POST reply. */ + int multiple_requests; /**< A flag which indicates if we use persistent connections. */ + uint8_t *post_data; + int post_datalen; + int is_akamai; + int rw_timeout; + char *mime_type; + char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) +} HTTPContext; + +#define OFFSET(x) offsetof(HTTPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +#define DEFAULT_USER_AGENT "Mozilla/5.0 Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION) +static const AVOption options[] = { +{"seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, D }, +{"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, +{"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"content_type", "force a content type", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D }, +{"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, +{"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, +{"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, +{"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, +{NULL} +}; +#define HTTP_CLASS(flavor)\ +static const AVClass flavor ## _context_class = {\ + .class_name = #flavor,\ + .item_name = av_default_item_name,\ + .option = options,\ + .version = LIBAVUTIL_VERSION_INT,\ +} + +HTTP_CLASS(http); +HTTP_CLASS(https); + +static int http_connect(URLContext *h, const char *path, const char *local_path, + const char *hoststr, const char *auth, + const char *proxyauth, int *new_location); + +void ff_http_init_auth_state(URLContext *dest, const URLContext *src) +{ + memcpy(&((HTTPContext*)dest->priv_data)->auth_state, + &((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState)); + memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state, + &((HTTPContext*)src->priv_data)->proxy_auth_state, + sizeof(HTTPAuthState)); +} + +/* return non zero if error */ +static int http_open_cnx(URLContext *h) +{ + const char *path, *proxy_path, *lower_proto = "tcp", *local_path; + char hostname[1024], hoststr[1024], proto[10]; + char auth[1024], proxyauth[1024] = ""; + char path1[MAX_URL_SIZE]; + char buf[1024], urlbuf[MAX_URL_SIZE]; + int port, use_proxy, err, location_changed = 0, redirects = 0, attempts = 0; + HTTPAuthType cur_auth_type, cur_proxy_auth_type; + HTTPContext *s = h->priv_data; + + /* fill the dest addr */ + redo: + /* needed in any case to build the host string */ + av_url_split(proto, sizeof(proto), auth, sizeof(auth), + hostname, sizeof(hostname), &port, + path1, sizeof(path1), s->location); + ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL); + + proxy_path = getenv("http_proxy"); + use_proxy = !ff_http_match_no_proxy(getenv("no_proxy"), hostname) && + proxy_path != NULL && av_strstart(proxy_path, "http://", NULL); + + if (!strcmp(proto, "https")) { + lower_proto = "tls"; + use_proxy = 0; + if (port < 0) + port = 443; + } + if (port < 0) + port = 80; + + if (path1[0] == '\0') + path = "/"; + else + path = path1; + local_path = path; + if (use_proxy) { + /* Reassemble the request URL without auth string - we don't + * want to leak the auth to the proxy. */ + ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s", + path1); + path = urlbuf; + av_url_split(NULL, 0, proxyauth, sizeof(proxyauth), + hostname, sizeof(hostname), &port, NULL, 0, proxy_path); + } + + ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); + + if (!s->hd) { + AVDictionary *opts = NULL; + char opts_format[20]; + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ + err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, &opts); + av_dict_free(&opts); + if (err < 0) + goto fail; + } + + cur_auth_type = s->auth_state.auth_type; + cur_proxy_auth_type = s->auth_state.auth_type; + if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0) + goto fail; + attempts++; + if (s->http_code == 401) { + if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) && + s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { + ffurl_closep(&s->hd); + goto redo; + } else + goto fail; + } + if (s->http_code == 407) { + if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && + s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { + ffurl_closep(&s->hd); + goto redo; + } else + goto fail; + } + if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307) + && location_changed == 1) { + /* url moved, get next */ + ffurl_closep(&s->hd); + if (redirects++ >= MAX_REDIRECTS) + return AVERROR(EIO); + /* Restart the authentication process with the new target, which + * might use a different auth mechanism. */ + memset(&s->auth_state, 0, sizeof(s->auth_state)); + attempts = 0; + location_changed = 0; + goto redo; + } + return 0; + fail: + if (s->hd) + ffurl_closep(&s->hd); + return AVERROR(EIO); +} + +int ff_http_do_new_request(URLContext *h, const char *uri) +{ + HTTPContext *s = h->priv_data; + + s->off = 0; + av_strlcpy(s->location, uri, sizeof(s->location)); + + return http_open_cnx(h); +} + +static int http_open(URLContext *h, const char *uri, int flags) +{ + HTTPContext *s = h->priv_data; + + if( s->seekable == 1 ) + h->is_streamed = 0; + else + h->is_streamed = 1; + + s->filesize = -1; + av_strlcpy(s->location, uri, sizeof(s->location)); + + if (s->headers) { + int len = strlen(s->headers); + if (len < 2 || strcmp("\r\n", s->headers + len - 2)) + av_log(h, AV_LOG_WARNING, "No trailing CRLF found in HTTP header.\n"); + } + + return http_open_cnx(h); +} +static int http_getc(HTTPContext *s) +{ + int len; + if (s->buf_ptr >= s->buf_end) { + len = ffurl_read(s->hd, s->buffer, BUFFER_SIZE); + if (len < 0) { + return len; + } else if (len == 0) { + return -1; + } else { + s->buf_ptr = s->buffer; + s->buf_end = s->buffer + len; + } + } + return *s->buf_ptr++; +} + +static int http_get_line(HTTPContext *s, char *line, int line_size) +{ + int ch; + char *q; + + q = line; + for(;;) { + ch = http_getc(s); + if (ch < 0) + return ch; + if (ch == '\n') { + /* process line */ + if (q > line && q[-1] == '\r') + q--; + *q = '\0'; + + return 0; + } else { + if ((q - line) < line_size - 1) + *q++ = ch; + } + } +} + +static int process_line(URLContext *h, char *line, int line_count, + int *new_location) +{ + HTTPContext *s = h->priv_data; + char *tag, *p, *end; + + /* end of header */ + if (line[0] == '\0') { + s->end_header = 1; + return 0; + } + + p = line; + if (line_count == 0) { + while (!av_isspace(*p) && *p != '\0') + p++; + while (av_isspace(*p)) + p++; + s->http_code = strtol(p, &end, 10); + + av_dlog(NULL, "http_code=%d\n", s->http_code); + + /* error codes are 4xx and 5xx, but regard 401 as a success, so we + * don't abort until all headers have been parsed. */ + if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401 + || s->auth_state.auth_type != HTTP_AUTH_NONE) && + (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) { + end += strspn(end, SPACE_CHARS); + av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n", + s->http_code, end); + return -1; + } + } else { + while (*p != '\0' && *p != ':') + p++; + if (*p != ':') + return 1; + + *p = '\0'; + tag = line; + p++; + while (av_isspace(*p)) + p++; + if (!av_strcasecmp(tag, "Location")) { + av_strlcpy(s->location, p, sizeof(s->location)); + *new_location = 1; + } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) { + s->filesize = strtoll(p, NULL, 10); + } else if (!av_strcasecmp (tag, "Content-Range")) { + /* "bytes $from-$to/$document_size" */ + const char *slash; + if (!strncmp (p, "bytes ", 6)) { + p += 6; + s->off = strtoll(p, NULL, 10); + if ((slash = strchr(p, '/')) && strlen(slash) > 0) + s->filesize = strtoll(slash+1, NULL, 10); + } + if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647)) + h->is_streamed = 0; /* we _can_ in fact seek */ + } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) { + h->is_streamed = 0; + } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) { + s->filesize = -1; + s->chunksize = 0; + } else if (!av_strcasecmp (tag, "WWW-Authenticate")) { + ff_http_auth_handle_header(&s->auth_state, tag, p); + } else if (!av_strcasecmp (tag, "Authentication-Info")) { + ff_http_auth_handle_header(&s->auth_state, tag, p); + } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) { + ff_http_auth_handle_header(&s->proxy_auth_state, tag, p); + } else if (!av_strcasecmp (tag, "Connection")) { + if (!strcmp(p, "close")) + s->willclose = 1; + } else if (!av_strcasecmp (tag, "Server") && !av_strcasecmp (p, "AkamaiGHost")) { + s->is_akamai = 1; + } else if (!av_strcasecmp (tag, "Content-Type")) { + av_free(s->mime_type); s->mime_type = av_strdup(p); + } else if (!av_strcasecmp (tag, "Set-Cookie")) { + if (!s->cookies) { + if (!(s->cookies = av_strdup(p))) + return AVERROR(ENOMEM); + } else { + char *tmp = s->cookies; + size_t str_size = strlen(tmp) + strlen(p) + 2; + if (!(s->cookies = av_malloc(str_size))) { + s->cookies = tmp; + return AVERROR(ENOMEM); + } + snprintf(s->cookies, str_size, "%s\n%s", tmp, p); + av_free(tmp); + } + } + } + return 1; +} + +/** + * Create a string containing cookie values for use as a HTTP cookie header + * field value for a particular path and domain from the cookie values stored in + * the HTTP protocol context. The cookie string is stored in *cookies. + * + * @return a negative value if an error condition occurred, 0 otherwise + */ +static int get_cookies(HTTPContext *s, char **cookies, const char *path, + const char *domain) +{ + // cookie strings will look like Set-Cookie header field values. Multiple + // Set-Cookie fields will result in multiple values delimited by a newline + int ret = 0; + char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies; + + if (!set_cookies) return AVERROR(EINVAL); + + *cookies = NULL; + while ((cookie = av_strtok(set_cookies, "\n", &next))) { + int domain_offset = 0; + char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL; + set_cookies = NULL; + + while ((param = av_strtok(cookie, "; ", &next_param))) { + cookie = NULL; + if (!av_strncasecmp("path=", param, 5)) { + av_free(cpath); + cpath = av_strdup(¶m[5]); + } else if (!av_strncasecmp("domain=", param, 7)) { + av_free(cdomain); + cdomain = av_strdup(¶m[7]); + } else if (!av_strncasecmp("secure", param, 6) || + !av_strncasecmp("comment", param, 7) || + !av_strncasecmp("max-age", param, 7) || + !av_strncasecmp("version", param, 7)) { + // ignore Comment, Max-Age, Secure and Version + } else { + av_free(cvalue); + cvalue = av_strdup(param); + } + } + + // ensure all of the necessary values are valid + if (!cdomain || !cpath || !cvalue) { + av_log(s, AV_LOG_WARNING, + "Invalid cookie found, no value, path or domain specified\n"); + goto done_cookie; + } + + // check if the request path matches the cookie path + if (av_strncasecmp(path, cpath, strlen(cpath))) + goto done_cookie; + + // the domain should be at least the size of our cookie domain + domain_offset = strlen(domain) - strlen(cdomain); + if (domain_offset < 0) + goto done_cookie; + + // match the cookie domain + if (av_strcasecmp(&domain[domain_offset], cdomain)) + goto done_cookie; + + // cookie parameters match, so copy the value + if (!*cookies) { + if (!(*cookies = av_strdup(cvalue))) { + ret = AVERROR(ENOMEM); + goto done_cookie; + } + } else { + char *tmp = *cookies; + size_t str_size = strlen(cvalue) + strlen(*cookies) + 3; + if (!(*cookies = av_malloc(str_size))) { + ret = AVERROR(ENOMEM); + goto done_cookie; + } + snprintf(*cookies, str_size, "%s; %s", tmp, cvalue); + av_free(tmp); + } + + done_cookie: + av_free(cdomain); + av_free(cpath); + av_free(cvalue); + if (ret < 0) { + if (*cookies) av_freep(cookies); + av_free(cset_cookies); + return ret; + } + } + + av_free(cset_cookies); + + return 0; +} + +static inline int has_header(const char *str, const char *header) +{ + /* header + 2 to skip over CRLF prefix. (make sure you have one!) */ + if (!str) + return 0; + return av_stristart(str, header + 2, NULL) || av_stristr(str, header); +} + +static int http_read_header(URLContext *h, int *new_location) +{ + HTTPContext *s = h->priv_data; + char line[MAX_URL_SIZE]; + int err = 0; + + s->chunksize = -1; + + for (;;) { + if ((err = http_get_line(s, line, sizeof(line))) < 0) + return err; + + av_dlog(NULL, "header='%s'\n", line); + + err = process_line(h, line, s->line_count, new_location); + if (err < 0) + return err; + if (err == 0) + break; + s->line_count++; + } + + return err; +} + +static int http_connect(URLContext *h, const char *path, const char *local_path, + const char *hoststr, const char *auth, + const char *proxyauth, int *new_location) +{ + HTTPContext *s = h->priv_data; + int post, err; + char headers[4096] = ""; + char *authstr = NULL, *proxyauthstr = NULL; + int64_t off = s->off; + int len = 0; + const char *method; + + + /* send http header */ + post = h->flags & AVIO_FLAG_WRITE; + + if (s->post_data) { + /* force POST method and disable chunked encoding when + * custom HTTP post data is set */ + post = 1; + s->chunked_post = 0; + } + + method = post ? "POST" : "GET"; + authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path, + method); + proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth, + local_path, method); + + /* set default headers if needed */ + if (!has_header(s->headers, "\r\nUser-Agent: ")) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "User-Agent: %s\r\n", s->user_agent); + if (!has_header(s->headers, "\r\nAccept: ")) + len += av_strlcpy(headers + len, "Accept: */*\r\n", + sizeof(headers) - len); + // Note: we send this on purpose even when s->off is 0 when we're probing, + // since it allows us to detect more reliably if a (non-conforming) + // server supports seeking by analysing the reply headers. + if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1)) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Range: bytes=%"PRId64"-\r\n", s->off); + + if (!has_header(s->headers, "\r\nConnection: ")) { + if (s->multiple_requests) { + len += av_strlcpy(headers + len, "Connection: keep-alive\r\n", + sizeof(headers) - len); + } else { + len += av_strlcpy(headers + len, "Connection: close\r\n", + sizeof(headers) - len); + } + } + + if (!has_header(s->headers, "\r\nHost: ")) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Host: %s\r\n", hoststr); + if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Content-Length: %d\r\n", s->post_datalen); + if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Content-Type: %s\r\n", s->content_type); + if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) { + char *cookies = NULL; + if (!get_cookies(s, &cookies, path, hoststr)) { + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Cookie: %s\r\n", cookies); + av_free(cookies); + } + } + + /* now add in custom headers */ + if (s->headers) + av_strlcpy(headers + len, s->headers, sizeof(headers) - len); + + snprintf(s->buffer, sizeof(s->buffer), + "%s %s HTTP/1.1\r\n" + "%s" + "%s" + "%s" + "%s%s" + "\r\n", + method, + path, + post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "", + headers, + authstr ? authstr : "", + proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : ""); + + av_freep(&authstr); + av_freep(&proxyauthstr); + if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) + return err; + + if (s->post_data) + if ((err = ffurl_write(s->hd, s->post_data, s->post_datalen)) < 0) + return err; + + /* init input buffer */ + s->buf_ptr = s->buffer; + s->buf_end = s->buffer; + s->line_count = 0; + s->off = 0; + s->filesize = -1; + s->willclose = 0; + s->end_chunked_post = 0; + s->end_header = 0; + if (post && !s->post_data) { + /* Pretend that it did work. We didn't read any header yet, since + * we've still to send the POST data, but the code calling this + * function will check http_code after we return. */ + s->http_code = 200; + return 0; + } + + /* wait for header */ + err = http_read_header(h, new_location); + if (err < 0) + return err; + + return (off == s->off) ? 0 : -1; +} + + +static int http_buf_read(URLContext *h, uint8_t *buf, int size) +{ + HTTPContext *s = h->priv_data; + int len; + /* read bytes from input buffer first */ + len = s->buf_end - s->buf_ptr; + if (len > 0) { + if (len > size) + len = size; + memcpy(buf, s->buf_ptr, len); + s->buf_ptr += len; + } else { + if (!s->willclose && s->filesize >= 0 && s->off >= s->filesize) + return AVERROR_EOF; + len = ffurl_read(s->hd, buf, size); + } + if (len > 0) { + s->off += len; + if (s->chunksize > 0) + s->chunksize -= len; + } + return len; +} + +static int http_read(URLContext *h, uint8_t *buf, int size) +{ + HTTPContext *s = h->priv_data; + int err, new_location; + + if (!s->hd) + return AVERROR_EOF; + + if (s->end_chunked_post && !s->end_header) { + err = http_read_header(h, &new_location); + if (err < 0) + return err; + } + + if (s->chunksize >= 0) { + if (!s->chunksize) { + char line[32]; + + for(;;) { + do { + if ((err = http_get_line(s, line, sizeof(line))) < 0) + return err; + } while (!*line); /* skip CR LF from last chunk */ + + s->chunksize = strtoll(line, NULL, 16); + + av_dlog(NULL, "Chunked encoding data size: %"PRId64"'\n", s->chunksize); + + if (!s->chunksize) + return 0; + break; + } + } + size = FFMIN(size, s->chunksize); + } + return http_buf_read(h, buf, size); +} + +/* used only when posting data */ +static int http_write(URLContext *h, const uint8_t *buf, int size) +{ + char temp[11] = ""; /* 32-bit hex + CRLF + nul */ + int ret; + char crlf[] = "\r\n"; + HTTPContext *s = h->priv_data; + + if (!s->chunked_post) { + /* non-chunked data is sent without any special encoding */ + return ffurl_write(s->hd, buf, size); + } + + /* silently ignore zero-size data since chunk encoding that would + * signal EOF */ + if (size > 0) { + /* upload data using chunked encoding */ + snprintf(temp, sizeof(temp), "%x\r\n", size); + + if ((ret = ffurl_write(s->hd, temp, strlen(temp))) < 0 || + (ret = ffurl_write(s->hd, buf, size)) < 0 || + (ret = ffurl_write(s->hd, crlf, sizeof(crlf) - 1)) < 0) + return ret; + } + return size; +} + +static int http_shutdown(URLContext *h, int flags) +{ + int ret = 0; + char footer[] = "0\r\n\r\n"; + HTTPContext *s = h->priv_data; + + /* signal end of chunked encoding if used */ + if ((flags & AVIO_FLAG_WRITE) && s->chunked_post) { + ret = ffurl_write(s->hd, footer, sizeof(footer) - 1); + ret = ret > 0 ? 0 : ret; + s->end_chunked_post = 1; + } + + return ret; +} + +static int http_close(URLContext *h) +{ + int ret = 0; + HTTPContext *s = h->priv_data; + + if (!s->end_chunked_post) { + /* Close the write direction by sending the end of chunked encoding. */ + ret = http_shutdown(h, h->flags); + } + + if (s->hd) + ffurl_closep(&s->hd); + return ret; +} + +static int64_t http_seek(URLContext *h, int64_t off, int whence) +{ + HTTPContext *s = h->priv_data; + URLContext *old_hd = s->hd; + int64_t old_off = s->off; + uint8_t old_buf[BUFFER_SIZE]; + int old_buf_size; + + if (whence == AVSEEK_SIZE) + return s->filesize; + else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed) + return -1; + + /* we save the old context in case the seek fails */ + old_buf_size = s->buf_end - s->buf_ptr; + memcpy(old_buf, s->buf_ptr, old_buf_size); + s->hd = NULL; + if (whence == SEEK_CUR) + off += s->off; + else if (whence == SEEK_END) + off += s->filesize; + s->off = off; + + /* if it fails, continue on old connection */ + if (http_open_cnx(h) < 0) { + memcpy(s->buffer, old_buf, old_buf_size); + s->buf_ptr = s->buffer; + s->buf_end = s->buffer + old_buf_size; + s->hd = old_hd; + s->off = old_off; + return -1; + } + ffurl_close(old_hd); + return off; +} + +static int +http_get_file_handle(URLContext *h) +{ + HTTPContext *s = h->priv_data; + return ffurl_get_file_handle(s->hd); +} + +#if CONFIG_HTTP_PROTOCOL +URLProtocol ff_http_protocol = { + .name = "http", + .url_open = http_open, + .url_read = http_read, + .url_write = http_write, + .url_seek = http_seek, + .url_close = http_close, + .url_get_file_handle = http_get_file_handle, + .url_shutdown = http_shutdown, + .priv_data_size = sizeof(HTTPContext), + .priv_data_class = &http_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; +#endif +#if CONFIG_HTTPS_PROTOCOL +URLProtocol ff_https_protocol = { + .name = "https", + .url_open = http_open, + .url_read = http_read, + .url_write = http_write, + .url_seek = http_seek, + .url_close = http_close, + .url_get_file_handle = http_get_file_handle, + .url_shutdown = http_shutdown, + .priv_data_size = sizeof(HTTPContext), + .priv_data_class = &https_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; +#endif + +#if CONFIG_HTTPPROXY_PROTOCOL +static int http_proxy_close(URLContext *h) +{ + HTTPContext *s = h->priv_data; + if (s->hd) + ffurl_closep(&s->hd); + return 0; +} + +static int http_proxy_open(URLContext *h, const char *uri, int flags) +{ + HTTPContext *s = h->priv_data; + char hostname[1024], hoststr[1024]; + char auth[1024], pathbuf[1024], *path; + char lower_url[100]; + int port, ret = 0, attempts = 0; + HTTPAuthType cur_auth_type; + char *authstr; + int new_loc; + AVDictionary *opts = NULL; + char opts_format[20]; + + if( s->seekable == 1 ) + h->is_streamed = 0; + else + h->is_streamed = 1; + + av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, + pathbuf, sizeof(pathbuf), uri); + ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL); + path = pathbuf; + if (*path == '/') + path++; + + ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, + NULL); +redo: + if (s->rw_timeout != -1) { + snprintf(opts_format, sizeof(opts_format), "%d", s->rw_timeout); + av_dict_set(&opts, "timeout", opts_format, 0); + } /* if option is not given, don't pass it and let tcp use its own default */ + ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, &opts); + av_dict_free(&opts); + if (ret < 0) + return ret; + + authstr = ff_http_auth_create_response(&s->proxy_auth_state, auth, + path, "CONNECT"); + snprintf(s->buffer, sizeof(s->buffer), + "CONNECT %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "%s%s" + "\r\n", + path, + hoststr, + authstr ? "Proxy-" : "", authstr ? authstr : ""); + av_freep(&authstr); + + if ((ret = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) + goto fail; + + s->buf_ptr = s->buffer; + s->buf_end = s->buffer; + s->line_count = 0; + s->filesize = -1; + cur_auth_type = s->proxy_auth_state.auth_type; + + /* Note: This uses buffering, potentially reading more than the + * HTTP header. If tunneling a protocol where the server starts + * the conversation, we might buffer part of that here, too. + * Reading that requires using the proper ffurl_read() function + * on this URLContext, not using the fd directly (as the tls + * protocol does). This shouldn't be an issue for tls though, + * since the client starts the conversation there, so there + * is no extra data that we might buffer up here. + */ + ret = http_read_header(h, &new_loc); + if (ret < 0) + goto fail; + + attempts++; + if (s->http_code == 407 && + (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && + s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) { + ffurl_closep(&s->hd); + goto redo; + } + + if (s->http_code < 400) + return 0; + ret = AVERROR(EIO); + +fail: + http_proxy_close(h); + return ret; +} + +static int http_proxy_write(URLContext *h, const uint8_t *buf, int size) +{ + HTTPContext *s = h->priv_data; + return ffurl_write(s->hd, buf, size); +} + +URLProtocol ff_httpproxy_protocol = { + .name = "httpproxy", + .url_open = http_proxy_open, + .url_read = http_buf_read, + .url_write = http_proxy_write, + .url_close = http_proxy_close, + .url_get_file_handle = http_get_file_handle, + .priv_data_size = sizeof(HTTPContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/http.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/http.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/http.o libavformat/http.o: libavformat/http.c \ + libavutil/avstring.h libavutil/attributes.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/network.h config.h libavutil/error.h \ + libavformat/os_support.h libavformat/http.h libavformat/url.h \ + libavformat/httpauth.h libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/http.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/http.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,48 @@ +/* + * HTTP definitions + * Copyright (c) 2010 Josh Allmann + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_HTTP_H +#define AVFORMAT_HTTP_H + +#include "url.h" + +/** + * Initialize the authentication state based on another HTTP URLContext. + * This can be used to pre-initialize the authentication parameters if + * they are known beforehand, to avoid having to do an initial failing + * request just to get the parameters. + * + * @param dest URL context whose authentication state gets updated + * @param src URL context whose authentication state gets copied + */ +void ff_http_init_auth_state(URLContext *dest, const URLContext *src); + +/** + * Send a new HTTP request, reusing the old connection. + * + * @param h pointer to the resource + * @param uri uri used to perform the request + * @return a negative value if an error condition occurred, 0 + * otherwise + */ +int ff_http_do_new_request(URLContext *h, const char *uri); + +#endif /* AVFORMAT_HTTP_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/http.o Binary file ffmpeg/libavformat/http.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/httpauth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/httpauth.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,287 @@ +/* + * HTTP authentication + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "httpauth.h" +#include "libavutil/base64.h" +#include "libavutil/avstring.h" +#include "internal.h" +#include "libavutil/random_seed.h" +#include "libavutil/md5.h" +#include "urldecode.h" +#include "avformat.h" + +static void handle_basic_params(HTTPAuthState *state, const char *key, + int key_len, char **dest, int *dest_len) +{ + if (!strncmp(key, "realm=", key_len)) { + *dest = state->realm; + *dest_len = sizeof(state->realm); + } +} + +static void handle_digest_params(HTTPAuthState *state, const char *key, + int key_len, char **dest, int *dest_len) +{ + DigestParams *digest = &state->digest_params; + + if (!strncmp(key, "realm=", key_len)) { + *dest = state->realm; + *dest_len = sizeof(state->realm); + } else if (!strncmp(key, "nonce=", key_len)) { + *dest = digest->nonce; + *dest_len = sizeof(digest->nonce); + } else if (!strncmp(key, "opaque=", key_len)) { + *dest = digest->opaque; + *dest_len = sizeof(digest->opaque); + } else if (!strncmp(key, "algorithm=", key_len)) { + *dest = digest->algorithm; + *dest_len = sizeof(digest->algorithm); + } else if (!strncmp(key, "qop=", key_len)) { + *dest = digest->qop; + *dest_len = sizeof(digest->qop); + } else if (!strncmp(key, "stale=", key_len)) { + *dest = digest->stale; + *dest_len = sizeof(digest->stale); + } +} + +static void handle_digest_update(HTTPAuthState *state, const char *key, + int key_len, char **dest, int *dest_len) +{ + DigestParams *digest = &state->digest_params; + + if (!strncmp(key, "nextnonce=", key_len)) { + *dest = digest->nonce; + *dest_len = sizeof(digest->nonce); + } +} + +static void choose_qop(char *qop, int size) +{ + char *ptr = strstr(qop, "auth"); + char *end = ptr + strlen("auth"); + + if (ptr && (!*end || av_isspace(*end) || *end == ',') && + (ptr == qop || av_isspace(ptr[-1]) || ptr[-1] == ',')) { + av_strlcpy(qop, "auth", size); + } else { + qop[0] = 0; + } +} + +void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, + const char *value) +{ + if (!strcmp(key, "WWW-Authenticate") || !strcmp(key, "Proxy-Authenticate")) { + const char *p; + if (av_stristart(value, "Basic ", &p) && + state->auth_type <= HTTP_AUTH_BASIC) { + state->auth_type = HTTP_AUTH_BASIC; + state->realm[0] = 0; + state->stale = 0; + ff_parse_key_value(p, (ff_parse_key_val_cb) handle_basic_params, + state); + } else if (av_stristart(value, "Digest ", &p) && + state->auth_type <= HTTP_AUTH_DIGEST) { + state->auth_type = HTTP_AUTH_DIGEST; + memset(&state->digest_params, 0, sizeof(DigestParams)); + state->realm[0] = 0; + state->stale = 0; + ff_parse_key_value(p, (ff_parse_key_val_cb) handle_digest_params, + state); + choose_qop(state->digest_params.qop, + sizeof(state->digest_params.qop)); + if (!av_strcasecmp(state->digest_params.stale, "true")) + state->stale = 1; + } + } else if (!strcmp(key, "Authentication-Info")) { + ff_parse_key_value(value, (ff_parse_key_val_cb) handle_digest_update, + state); + } +} + + +static void update_md5_strings(struct AVMD5 *md5ctx, ...) +{ + va_list vl; + + va_start(vl, md5ctx); + while (1) { + const char* str = va_arg(vl, const char*); + if (!str) + break; + av_md5_update(md5ctx, str, strlen(str)); + } + va_end(vl); +} + +/* Generate a digest reply, according to RFC 2617. */ +static char *make_digest_auth(HTTPAuthState *state, const char *username, + const char *password, const char *uri, + const char *method) +{ + DigestParams *digest = &state->digest_params; + int len; + uint32_t cnonce_buf[2]; + char cnonce[17]; + char nc[9]; + int i; + char A1hash[33], A2hash[33], response[33]; + struct AVMD5 *md5ctx; + uint8_t hash[16]; + char *authstr; + + digest->nc++; + snprintf(nc, sizeof(nc), "%08x", digest->nc); + + /* Generate a client nonce. */ + for (i = 0; i < 2; i++) + cnonce_buf[i] = av_get_random_seed(); + ff_data_to_hex(cnonce, (const uint8_t*) cnonce_buf, sizeof(cnonce_buf), 1); + cnonce[2*sizeof(cnonce_buf)] = 0; + + md5ctx = av_md5_alloc(); + if (!md5ctx) + return NULL; + + av_md5_init(md5ctx); + update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(A1hash, hash, 16, 1); + A1hash[32] = 0; + + if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) { + } else if (!strcmp(digest->algorithm, "MD5-sess")) { + av_md5_init(md5ctx); + update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(A1hash, hash, 16, 1); + A1hash[32] = 0; + } else { + /* Unsupported algorithm */ + av_free(md5ctx); + return NULL; + } + + av_md5_init(md5ctx); + update_md5_strings(md5ctx, method, ":", uri, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(A2hash, hash, 16, 1); + A2hash[32] = 0; + + av_md5_init(md5ctx); + update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL); + if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) { + update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL); + } + update_md5_strings(md5ctx, ":", A2hash, NULL); + av_md5_final(md5ctx, hash); + ff_data_to_hex(response, hash, 16, 1); + response[32] = 0; + + av_free(md5ctx); + + if (!strcmp(digest->qop, "") || !strcmp(digest->qop, "auth")) { + } else if (!strcmp(digest->qop, "auth-int")) { + /* qop=auth-int not supported */ + return NULL; + } else { + /* Unsupported qop value. */ + return NULL; + } + + len = strlen(username) + strlen(state->realm) + strlen(digest->nonce) + + strlen(uri) + strlen(response) + strlen(digest->algorithm) + + strlen(digest->opaque) + strlen(digest->qop) + strlen(cnonce) + + strlen(nc) + 150; + + authstr = av_malloc(len); + if (!authstr) + return NULL; + snprintf(authstr, len, "Authorization: Digest "); + + /* TODO: Escape the quoted strings properly. */ + av_strlcatf(authstr, len, "username=\"%s\"", username); + av_strlcatf(authstr, len, ",realm=\"%s\"", state->realm); + av_strlcatf(authstr, len, ",nonce=\"%s\"", digest->nonce); + av_strlcatf(authstr, len, ",uri=\"%s\"", uri); + av_strlcatf(authstr, len, ",response=\"%s\"", response); + if (digest->algorithm[0]) + av_strlcatf(authstr, len, ",algorithm=%s", digest->algorithm); + if (digest->opaque[0]) + av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque); + if (digest->qop[0]) { + av_strlcatf(authstr, len, ",qop=\"%s\"", digest->qop); + av_strlcatf(authstr, len, ",cnonce=\"%s\"", cnonce); + av_strlcatf(authstr, len, ",nc=%s", nc); + } + + av_strlcatf(authstr, len, "\r\n"); + + return authstr; +} + +char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, + const char *path, const char *method) +{ + char *authstr = NULL; + + /* Clear the stale flag, we assume the auth is ok now. It is reset + * by the server headers if there's a new issue. */ + state->stale = 0; + if (!auth || !strchr(auth, ':')) + return NULL; + + if (state->auth_type == HTTP_AUTH_BASIC) { + int auth_b64_len, len; + char *ptr, *decoded_auth = ff_urldecode(auth); + + if (!decoded_auth) + return NULL; + + auth_b64_len = AV_BASE64_SIZE(strlen(decoded_auth)); + len = auth_b64_len + 30; + + authstr = av_malloc(len); + if (!authstr) { + av_free(decoded_auth); + return NULL; + } + + snprintf(authstr, len, "Authorization: Basic "); + ptr = authstr + strlen(authstr); + av_base64_encode(ptr, auth_b64_len, decoded_auth, strlen(decoded_auth)); + av_strlcat(ptr, "\r\n", len - (ptr - authstr)); + av_free(decoded_auth); + } else if (state->auth_type == HTTP_AUTH_DIGEST) { + char *username = ff_urldecode(auth), *password; + + if (!username) + return NULL; + + if ((password = strchr(username, ':'))) { + *password++ = 0; + authstr = make_digest_auth(state, username, password, path, method); + } + av_free(username); + } + return authstr; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/httpauth.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/httpauth.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/httpauth.o libavformat/httpauth.o: libavformat/httpauth.c \ + libavformat/httpauth.h libavutil/base64.h libavutil/avstring.h \ + libavutil/attributes.h libavformat/internal.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/random_seed.h \ + libavutil/md5.h libavformat/urldecode.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/httpauth.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/httpauth.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,79 @@ +/* + * HTTP authentication + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_HTTPAUTH_H +#define AVFORMAT_HTTPAUTH_H + +/** + * Authentication types, ordered from weakest to strongest. + */ +typedef enum HTTPAuthType { + HTTP_AUTH_NONE = 0, /**< No authentication specified */ + HTTP_AUTH_BASIC, /**< HTTP 1.0 Basic auth from RFC 1945 + * (also in RFC 2617) */ + HTTP_AUTH_DIGEST, /**< HTTP 1.1 Digest auth from RFC 2617 */ +} HTTPAuthType; + +typedef struct DigestParams { + char nonce[300]; /**< Server specified nonce */ + char algorithm[10]; /**< Server specified digest algorithm */ + char qop[30]; /**< Quality of protection, containing the one + * that we've chosen to use, from the + * alternatives that the server offered. */ + char opaque[300]; /**< A server-specified string that should be + * included in authentication responses, not + * included in the actual digest calculation. */ + char stale[10]; /**< The server indicated that the auth was ok, + * but needs to be redone with a new, non-stale + * nonce. */ + int nc; /**< Nonce count, the number of earlier replies + * where this particular nonce has been used. */ +} DigestParams; + +/** + * HTTP Authentication state structure. Must be zero-initialized + * before used with the functions below. + */ +typedef struct HTTPAuthState { + /** + * The currently chosen auth type. + */ + HTTPAuthType auth_type; + /** + * Authentication realm + */ + char realm[200]; + /** + * The parameters specifiec to digest authentication. + */ + DigestParams digest_params; + /** + * Auth ok, but needs to be resent with a new nonce. + */ + int stale; +} HTTPAuthState; + +void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, + const char *value); +char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, + const char *path, const char *method); + +#endif /* AVFORMAT_HTTPAUTH_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/httpauth.o Binary file ffmpeg/libavformat/httpauth.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icodec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/icodec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,182 @@ +/* + * Microsoft Windows ICO demuxer + * Copyright (c) 2011 Peter Ross (pross@xvid.org) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Microsoft Windows ICO demuxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/bmp.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int offset; + int size; + int nb_pal; +} IcoImage; + +typedef struct { + int current_image; + int nb_images; + IcoImage * images; +} IcoDemuxContext; + +static int probe(AVProbeData *p) +{ + if (AV_RL16(p->buf) == 0 && AV_RL16(p->buf + 2) == 1 && AV_RL16(p->buf + 4)) + return AVPROBE_SCORE_MAX / 3; + return 0; +} + +static int read_header(AVFormatContext *s) +{ + IcoDemuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int i, codec; + + avio_skip(pb, 4); + ico->nb_images = avio_rl16(pb); + + ico->images = av_malloc(ico->nb_images * sizeof(IcoImage)); + if (!ico->images) + return AVERROR(ENOMEM); + + for (i = 0; i < ico->nb_images; i++) { + AVStream *st; + int tmp; + + if (avio_seek(pb, 6 + i * 16, SEEK_SET) < 0) + break; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->width = avio_r8(pb); + st->codec->height = avio_r8(pb); + ico->images[i].nb_pal = avio_r8(pb); + if (ico->images[i].nb_pal == 255) + ico->images[i].nb_pal = 0; + + avio_skip(pb, 5); + + ico->images[i].size = avio_rl32(pb); + ico->images[i].offset = avio_rl32(pb); + + if (avio_seek(pb, ico->images[i].offset, SEEK_SET) < 0) + break; + + codec = avio_rl32(pb); + switch (codec) { + case MKTAG(0x89, 'P', 'N', 'G'): + st->codec->codec_id = AV_CODEC_ID_PNG; + st->codec->width = 0; + st->codec->height = 0; + break; + case 40: + if (ico->images[i].size < 40) + return AVERROR_INVALIDDATA; + st->codec->codec_id = AV_CODEC_ID_BMP; + tmp = avio_rl32(pb); + if (tmp) + st->codec->width = tmp; + tmp = avio_rl32(pb); + if (tmp) + st->codec->height = tmp / 2; + break; + default: + avpriv_request_sample(s, "codec %d", codec); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoDemuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int ret; + + if (ico->current_image >= ico->nb_images) + return AVERROR(EIO); + + image = &ico->images[ico->current_image]; + + if ((ret = avio_seek(pb, image->offset, SEEK_SET)) < 0) + return ret; + + if (s->streams[ico->current_image]->codec->codec_id == AV_CODEC_ID_PNG) { + if ((ret = av_get_packet(pb, pkt, image->size)) < 0) + return ret; + } else { + uint8_t *buf; + if ((ret = av_new_packet(pkt, 14 + image->size)) < 0) + return ret; + buf = pkt->data; + + /* add BMP header */ + bytestream_put_byte(&buf, 'B'); + bytestream_put_byte(&buf, 'M'); + bytestream_put_le32(&buf, pkt->size); + bytestream_put_le16(&buf, 0); + bytestream_put_le16(&buf, 0); + bytestream_put_le32(&buf, 0); + + if ((ret = avio_read(pb, buf, image->size)) < 0) + return ret; + + st->codec->bits_per_coded_sample = AV_RL16(buf + 14); + + if (AV_RL32(buf + 32)) + image->nb_pal = AV_RL32(buf + 32); + + if (st->codec->bits_per_coded_sample <= 8 && !image->nb_pal) { + image->nb_pal = 1 << st->codec->bits_per_coded_sample; + AV_WL32(buf + 32, image->nb_pal); + } + + AV_WL32(buf - 4, 14 + 40 + image->nb_pal * 4); + AV_WL32(buf + 8, AV_RL32(buf + 8) / 2); + } + + pkt->stream_index = ico->current_image++; + pkt->flags |= AV_PKT_FLAG_KEY; + + return 0; +} + +AVInputFormat ff_ico_demuxer = { + .name = "ico", + .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .priv_data_size = sizeof(IcoDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icodec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/icodec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/icodec.o libavformat/icodec.o: libavformat/icodec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavcodec/bytestream.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavcodec/bmp.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icodec.o Binary file ffmpeg/libavformat/icodec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icoenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/icoenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,202 @@ +/* + * Microsoft Windows ICO muxer + * Copyright (c) 2012 Michael Bradshaw + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Microsoft Windows ICO muxer + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" +#include "avformat.h" + +typedef struct { + int offset; + int size; + unsigned char width; + unsigned char height; + short bits; +} IcoImage; + +typedef struct { + int current_image; + int nb_images; + IcoImage *images; +} IcoMuxContext; + +static int ico_check_attributes(AVFormatContext *s, const AVCodecContext *c) +{ + if (c->codec_id == AV_CODEC_ID_BMP) { + if (c->pix_fmt == AV_PIX_FMT_PAL8 && AV_PIX_FMT_RGB32 != AV_PIX_FMT_BGRA) { + av_log(s, AV_LOG_ERROR, "Wrong endianness for bmp pixel format\n"); + return AVERROR(EINVAL); + } else if (c->pix_fmt != AV_PIX_FMT_PAL8 && + c->pix_fmt != AV_PIX_FMT_RGB555LE && + c->pix_fmt != AV_PIX_FMT_BGR24 && + c->pix_fmt != AV_PIX_FMT_BGRA) { + av_log(s, AV_LOG_ERROR, "BMP must be 1bit, 4bit, 8bit, 16bit, 24bit, or 32bit\n"); + return AVERROR(EINVAL); + } + } else if (c->codec_id == AV_CODEC_ID_PNG) { + if (c->pix_fmt != AV_PIX_FMT_RGBA) { + av_log(s, AV_LOG_ERROR, "PNG in ico requires pixel format to be rgba\n"); + return AVERROR(EINVAL); + } + } else { + av_log(s, AV_LOG_ERROR, "Unsupported codec %s\n", c->codec_name); + return AVERROR(EINVAL); + } + + if (c->width > 256 || + c->height > 256) { + av_log(s, AV_LOG_ERROR, "Unsupported dimensions %dx%d (dimensions cannot exceed 256x256)\n", c->width, c->height); + return AVERROR(EINVAL); + } + + return 0; +} + +static int ico_write_header(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int i; + + if (!pb->seekable) { + av_log(s, AV_LOG_ERROR, "Output is not seekable\n"); + return AVERROR(EINVAL); + } + + ico->current_image = 0; + ico->nb_images = s->nb_streams; + + avio_wl16(pb, 0); // reserved + avio_wl16(pb, 1); // 1 == icon + avio_skip(pb, 2); // skip the number of images + + for (i = 0; i < s->nb_streams; i++) { + if (ret = ico_check_attributes(s, s->streams[i]->codec)) + return ret; + + // Fill in later when writing trailer... + avio_skip(pb, 16); + } + + ico->images = av_mallocz(ico->nb_images * sizeof(IcoMuxContext)); + if (!ico->images) + return AVERROR(ENOMEM); + + avio_flush(pb); + + return 0; +} + +static int ico_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + IcoMuxContext *ico = s->priv_data; + IcoImage *image; + AVIOContext *pb = s->pb; + AVCodecContext *c = s->streams[pkt->stream_index]->codec; + int i; + + if (ico->current_image >= ico->nb_images) { + av_log(s, AV_LOG_ERROR, "ICO already contains %d images\n", ico->current_image); + return AVERROR(EIO); + } + + image = &ico->images[ico->current_image++]; + + image->offset = avio_tell(pb); + image->width = (c->width == 256) ? 0 : c->width; + image->height = (c->height == 256) ? 0 : c->height; + + if (c->codec_id == AV_CODEC_ID_PNG) { + image->bits = c->bits_per_coded_sample; + image->size = pkt->size; + + avio_write(pb, pkt->data, pkt->size); + } else { // BMP + if (AV_RL32(pkt->data + 14) != 40) { // must be BITMAPINFOHEADER + av_log(s, AV_LOG_ERROR, "Invalid BMP\n"); + return AVERROR(EINVAL); + } + + image->bits = AV_RL16(pkt->data + 28); // allows things like 1bit and 4bit images to be preserved + image->size = pkt->size - 14 + c->height * (c->width + 7) / 8; + + avio_write(pb, pkt->data + 14, 8); // Skip the BITMAPFILEHEADER header + avio_wl32(pb, AV_RL32(pkt->data + 22) * 2); // rewrite height as 2 * height + avio_write(pb, pkt->data + 26, pkt->size - 26); + + for (i = 0; i < c->height * (c->width + 7) / 8; ++i) + avio_w8(pb, 0x00); // Write bitmask (opaque) + } + + return 0; +} + +static int ico_write_trailer(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + AVIOContext *pb = s->pb; + int i; + + avio_seek(pb, 4, SEEK_SET); + + avio_wl16(pb, ico->current_image); + + for (i = 0; i < ico->nb_images; i++) { + avio_w8(pb, ico->images[i].width); + avio_w8(pb, ico->images[i].height); + + if (s->streams[i]->codec->codec_id == AV_CODEC_ID_BMP && + s->streams[i]->codec->pix_fmt == AV_PIX_FMT_PAL8) { + avio_w8(pb, (ico->images[i].bits >= 8) ? 0 : 1 << ico->images[i].bits); + } else { + avio_w8(pb, 0); + } + + avio_w8(pb, 0); // reserved + avio_wl16(pb, 1); // color planes + avio_wl16(pb, ico->images[i].bits); + avio_wl32(pb, ico->images[i].size); + avio_wl32(pb, ico->images[i].offset); + } + + av_freep(&ico->images); + + return 0; +} + +AVOutputFormat ff_ico_muxer = { + .name = "ico", + .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .priv_data_size = sizeof(IcoMuxContext), + .mime_type = "image/vnd.microsoft.icon", + .extensions = "ico", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_BMP, + .write_header = ico_write_header, + .write_packet = ico_write_packet, + .write_trailer = ico_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icoenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/icoenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/icoenc.o libavformat/icoenc.o: libavformat/icoenc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavutil/pixdesc.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/icoenc.o Binary file ffmpeg/libavformat/icoenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v1.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,246 @@ +/* + * ID3v1 header parser + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "id3v1.h" +#include "libavcodec/avcodec.h" +#include "libavutil/dict.h" + +/* See Genre List at http://id3.org/id3v2.3.0 */ +const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { + [0] = "Blues", + [1] = "Classic Rock", + [2] = "Country", + [3] = "Dance", + [4] = "Disco", + [5] = "Funk", + [6] = "Grunge", + [7] = "Hip-Hop", + [8] = "Jazz", + [9] = "Metal", + [10] = "New Age", + [11] = "Oldies", + [12] = "Other", + [13] = "Pop", + [14] = "R&B", + [15] = "Rap", + [16] = "Reggae", + [17] = "Rock", + [18] = "Techno", + [19] = "Industrial", + [20] = "Alternative", + [21] = "Ska", + [22] = "Death Metal", + [23] = "Pranks", + [24] = "Soundtrack", + [25] = "Euro-Techno", + [26] = "Ambient", + [27] = "Trip-Hop", + [28] = "Vocal", + [29] = "Jazz+Funk", + [30] = "Fusion", + [31] = "Trance", + [32] = "Classical", + [33] = "Instrumental", + [34] = "Acid", + [35] = "House", + [36] = "Game", + [37] = "Sound Clip", + [38] = "Gospel", + [39] = "Noise", + [40] = "AlternRock", + [41] = "Bass", + [42] = "Soul", + [43] = "Punk", + [44] = "Space", + [45] = "Meditative", + [46] = "Instrumental Pop", + [47] = "Instrumental Rock", + [48] = "Ethnic", + [49] = "Gothic", + [50] = "Darkwave", + [51] = "Techno-Industrial", + [52] = "Electronic", + [53] = "Pop-Folk", + [54] = "Eurodance", + [55] = "Dream", + [56] = "Southern Rock", + [57] = "Comedy", + [58] = "Cult", + [59] = "Gangsta", + [60] = "Top 40", + [61] = "Christian Rap", + [62] = "Pop/Funk", + [63] = "Jungle", + [64] = "Native American", + [65] = "Cabaret", + [66] = "New Wave", + [67] = "Psychadelic", /* sic, the misspelling is used in the specification */ + [68] = "Rave", + [69] = "Showtunes", + [70] = "Trailer", + [71] = "Lo-Fi", + [72] = "Tribal", + [73] = "Acid Punk", + [74] = "Acid Jazz", + [75] = "Polka", + [76] = "Retro", + [77] = "Musical", + [78] = "Rock & Roll", + [79] = "Hard Rock", + [80] = "Folk", + [81] = "Folk-Rock", + [82] = "National Folk", + [83] = "Swing", + [84] = "Fast Fusion", + [85] = "Bebob", + [86] = "Latin", + [87] = "Revival", + [88] = "Celtic", + [89] = "Bluegrass", + [90] = "Avantgarde", + [91] = "Gothic Rock", + [92] = "Progressive Rock", + [93] = "Psychedelic Rock", + [94] = "Symphonic Rock", + [95] = "Slow Rock", + [96] = "Big Band", + [97] = "Chorus", + [98] = "Easy Listening", + [99] = "Acoustic", + [100] = "Humour", + [101] = "Speech", + [102] = "Chanson", + [103] = "Opera", + [104] = "Chamber Music", + [105] = "Sonata", + [106] = "Symphony", + [107] = "Booty Bass", + [108] = "Primus", + [109] = "Porn Groove", + [110] = "Satire", + [111] = "Slow Jam", + [112] = "Club", + [113] = "Tango", + [114] = "Samba", + [115] = "Folklore", + [116] = "Ballad", + [117] = "Power Ballad", + [118] = "Rhythmic Soul", + [119] = "Freestyle", + [120] = "Duet", + [121] = "Punk Rock", + [122] = "Drum Solo", + [123] = "A capella", + [124] = "Euro-House", + [125] = "Dance Hall", + [126] = "Goa", + [127] = "Drum & Bass", + [128] = "Club-House", + [129] = "Hardcore", + [130] = "Terror", + [131] = "Indie", + [132] = "BritPop", + [133] = "Negerpunk", + [134] = "Polsk Punk", + [135] = "Beat", + [136] = "Christian Gangsta", + [137] = "Heavy Metal", + [138] = "Black Metal", + [139] = "Crossover", + [140] = "Contemporary Christian", + [141] = "Christian Rock", + [142] = "Merengue", + [143] = "Salsa", + [144] = "Thrash Metal", + [145] = "Anime", + [146] = "JPop", + [147] = "SynthPop", +}; + +static void get_string(AVFormatContext *s, const char *key, + const uint8_t *buf, int buf_size) +{ + int i, c; + char *q, str[512]; + + q = str; + for(i = 0; i < buf_size; i++) { + c = buf[i]; + if (c == '\0') + break; + if ((q - str) >= sizeof(str) - 1) + break; + *q++ = c; + } + *q = '\0'; + + if (*str) + av_dict_set(&s->metadata, key, str, 0); +} + +/** + * Parse an ID3v1 tag + * + * @param buf ID3v1_TAG_SIZE long buffer containing the tag + */ +static int parse_tag(AVFormatContext *s, const uint8_t *buf) +{ + char str[5]; + int genre; + + if (!(buf[0] == 'T' && + buf[1] == 'A' && + buf[2] == 'G')) + return -1; + get_string(s, "title", buf + 3, 30); + get_string(s, "artist", buf + 33, 30); + get_string(s, "album", buf + 63, 30); + get_string(s, "date", buf + 93, 4); + get_string(s, "comment", buf + 97, 30); + if (buf[125] == 0 && buf[126] != 0) { + snprintf(str, sizeof(str), "%d", buf[126]); + av_dict_set(&s->metadata, "track", str, 0); + } + genre = buf[127]; + if (genre <= ID3v1_GENRE_MAX) + av_dict_set(&s->metadata, "genre", ff_id3v1_genre_str[genre], 0); + return 0; +} + +void ff_id3v1_read(AVFormatContext *s) +{ + int ret; + uint8_t buf[ID3v1_TAG_SIZE]; + int64_t filesize, position = avio_tell(s->pb); + + if (s->pb->seekable) { + /* XXX: change that */ + filesize = avio_size(s->pb); + if (filesize > 128) { + avio_seek(s->pb, filesize - 128, SEEK_SET); + ret = avio_read(s->pb, buf, ID3v1_TAG_SIZE); + if (ret == ID3v1_TAG_SIZE) { + parse_tag(s, buf); + } + avio_seek(s->pb, position, SEEK_SET); + } + } +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v1.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v1.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/id3v1.o libavformat/id3v1.o: libavformat/id3v1.c \ + libavformat/id3v1.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v1.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v1.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,41 @@ +/* + * ID3v1 header parser + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_ID3V1_H +#define AVFORMAT_ID3V1_H + +#include "avformat.h" + +#define ID3v1_TAG_SIZE 128 + +#define ID3v1_GENRE_MAX 147 + +/** + * ID3v1 genres + */ +extern const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1]; + +/** + * Read an ID3v1 tag + */ +void ff_id3v1_read(AVFormatContext *s); + +#endif /* AVFORMAT_ID3V1_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v1.o Binary file ffmpeg/libavformat/id3v1.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v2.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * ID3v2 header parser + * + * Specifications available at: + * http://id3.org/Developer_Information + */ + +#include "config.h" + +#if CONFIG_ZLIB +#include +#endif + +#include "id3v2.h" +#include "id3v1.h" +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "avio_internal.h" +#include "internal.h" + +const AVMetadataConv ff_id3v2_34_metadata_conv[] = { + { "TALB", "album"}, + { "TCOM", "composer"}, + { "TCON", "genre"}, + { "TCOP", "copyright"}, + { "TENC", "encoded_by"}, + { "TIT2", "title"}, + { "TLAN", "language"}, + { "TPE1", "artist"}, + { "TPE2", "album_artist"}, + { "TPE3", "performer"}, + { "TPOS", "disc"}, + { "TPUB", "publisher"}, + { "TRCK", "track"}, + { "TSSE", "encoder"}, + { 0 } +}; + +const AVMetadataConv ff_id3v2_4_metadata_conv[] = { + { "TDRL", "date"}, + { "TDRC", "date"}, + { "TDEN", "creation_time"}, + { "TSOA", "album-sort"}, + { "TSOP", "artist-sort"}, + { "TSOT", "title-sort"}, + { 0 } +}; + +static const AVMetadataConv id3v2_2_metadata_conv[] = { + { "TAL", "album"}, + { "TCO", "genre"}, + { "TT2", "title"}, + { "TEN", "encoded_by"}, + { "TP1", "artist"}, + { "TP2", "album_artist"}, + { "TP3", "performer"}, + { "TRK", "track"}, + { 0 } +}; + + +const char ff_id3v2_tags[][4] = { + "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", + "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", + "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", + "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", + { 0 }, +}; + +const char ff_id3v2_4_tags[][4] = { + "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO", + "TPRO", "TSOA", "TSOP", "TSOT", "TSST", + { 0 }, +}; + +const char ff_id3v2_3_tags[][4] = { + "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER", + { 0 }, +}; + +const char *ff_id3v2_picture_types[21] = { + "Other", + "32x32 pixels 'file icon'", + "Other file icon", + "Cover (front)", + "Cover (back)", + "Leaflet page", + "Media (e.g. label side of CD)", + "Lead artist/lead performer/soloist", + "Artist/performer", + "Conductor", + "Band/Orchestra", + "Composer", + "Lyricist/text writer", + "Recording Location", + "During recording", + "During performance", + "Movie/video screen capture", + "A bright coloured fish", + "Illustration", + "Band/artist logotype", + "Publisher/Studio logotype", +}; + +const CodecMime ff_id3v2_mime_tags[] = { + {"image/gif" , AV_CODEC_ID_GIF}, + {"image/jpeg", AV_CODEC_ID_MJPEG}, + {"image/jpg", AV_CODEC_ID_MJPEG}, + {"image/png" , AV_CODEC_ID_PNG}, + {"image/tiff", AV_CODEC_ID_TIFF}, + {"image/bmp", AV_CODEC_ID_BMP}, + {"JPG", AV_CODEC_ID_MJPEG}, /* ID3v2.2 */ + {"PNG" , AV_CODEC_ID_PNG}, /* ID3v2.2 */ + {"", AV_CODEC_ID_NONE}, +}; + +int ff_id3v2_match(const uint8_t *buf, const char * magic) +{ + return buf[0] == magic[0] && + buf[1] == magic[1] && + buf[2] == magic[2] && + buf[3] != 0xff && + buf[4] != 0xff && + (buf[6] & 0x80) == 0 && + (buf[7] & 0x80) == 0 && + (buf[8] & 0x80) == 0 && + (buf[9] & 0x80) == 0; +} + +int ff_id3v2_tag_len(const uint8_t * buf) +{ + int len = ((buf[6] & 0x7f) << 21) + + ((buf[7] & 0x7f) << 14) + + ((buf[8] & 0x7f) << 7) + + (buf[9] & 0x7f) + + ID3v2_HEADER_SIZE; + if (buf[5] & 0x10) + len += ID3v2_HEADER_SIZE; + return len; +} + +static unsigned int get_size(AVIOContext *s, int len) +{ + int v = 0; + while (len--) + v = (v << 7) + (avio_r8(s) & 0x7F); + return v; +} + +/** + * Free GEOB type extra metadata. + */ +static void free_geobtag(void *obj) +{ + ID3v2ExtraMetaGEOB *geob = obj; + av_free(geob->mime_type); + av_free(geob->file_name); + av_free(geob->description); + av_free(geob->data); + av_free(geob); +} + +/** + * Decode characters to UTF-8 according to encoding type. The decoded buffer is + * always null terminated. Stop reading when either *maxread bytes are read from + * pb or U+0000 character is found. + * + * @param dst Pointer where the address of the buffer with the decoded bytes is + * stored. Buffer must be freed by caller. + * @param maxread Pointer to maximum number of characters to read from the + * AVIOContext. After execution the value is decremented by the number of bytes + * actually read. + * @returns 0 if no error occurred, dst is uninitialized on error + */ +static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, + uint8_t **dst, int *maxread) +{ + int ret; + uint8_t tmp; + uint32_t ch = 1; + int left = *maxread; + unsigned int (*get)(AVIOContext*) = avio_rb16; + AVIOContext *dynbuf; + + if ((ret = avio_open_dyn_buf(&dynbuf)) < 0) { + av_log(s, AV_LOG_ERROR, "Error opening memory stream\n"); + return ret; + } + + switch (encoding) { + + case ID3v2_ENCODING_ISO8859: + while (left && ch) { + ch = avio_r8(pb); + PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);) + left--; + } + break; + + case ID3v2_ENCODING_UTF16BOM: + if ((left -= 2) < 0) { + av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n"); + avio_close_dyn_buf(dynbuf, dst); + av_freep(dst); + return AVERROR_INVALIDDATA; + } + switch (avio_rb16(pb)) { + case 0xfffe: + get = avio_rl16; + case 0xfeff: + break; + default: + av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n"); + avio_close_dyn_buf(dynbuf, dst); + av_freep(dst); + *maxread = left; + return AVERROR_INVALIDDATA; + } + // fall-through + + case ID3v2_ENCODING_UTF16BE: + while ((left > 1) && ch) { + GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;) + PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);) + } + if (left < 0) + left += 2; /* did not read last char from pb */ + break; + + case ID3v2_ENCODING_UTF8: + while (left && ch) { + ch = avio_r8(pb); + avio_w8(dynbuf, ch); + left--; + } + break; + default: + av_log(s, AV_LOG_WARNING, "Unknown encoding\n"); + } + + if (ch) + avio_w8(dynbuf, 0); + + avio_close_dyn_buf(dynbuf, dst); + *maxread = left; + + return 0; +} + +/** + * Parse a text tag. + */ +static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key) +{ + uint8_t *dst; + int encoding, dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; + unsigned genre; + + if (taglen < 1) + return; + + encoding = avio_r8(pb); + taglen--; /* account for encoding type byte */ + + if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { + av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key); + return; + } + + if (!(strcmp(key, "TCON") && strcmp(key, "TCO")) + && (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1) + && genre <= ID3v1_GENRE_MAX) { + av_freep(&dst); + dst = av_strdup(ff_id3v1_genre_str[genre]); + } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) { + /* dst now contains the key, need to get value */ + key = dst; + if (decode_str(s, pb, encoding, &dst, &taglen) < 0) { + av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key); + av_freep(&key); + return; + } + dict_flags |= AV_DICT_DONT_STRDUP_KEY; + } else if (!*dst) + av_freep(&dst); + + if (dst) + av_dict_set(&s->metadata, key, dst, dict_flags); +} + +/** + * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct. + */ +static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta) +{ + ID3v2ExtraMetaGEOB *geob_data = NULL; + ID3v2ExtraMeta *new_extra = NULL; + char encoding; + unsigned int len; + + if (taglen < 1) + return; + + geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB)); + if (!geob_data) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMetaGEOB)); + return; + } + + new_extra = av_mallocz(sizeof(ID3v2ExtraMeta)); + if (!new_extra) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %zu bytes\n", sizeof(ID3v2ExtraMeta)); + goto fail; + } + + /* read encoding type byte */ + encoding = avio_r8(pb); + taglen--; + + /* read MIME type (always ISO-8859) */ + if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, &taglen) < 0 + || taglen <= 0) + goto fail; + + /* read file name */ + if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0 + || taglen <= 0) + goto fail; + + /* read content description */ + if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0 + || taglen < 0) + goto fail; + + if (taglen) { + /* save encapsulated binary data */ + geob_data->data = av_malloc(taglen); + if (!geob_data->data) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", taglen); + goto fail; + } + if ((len = avio_read(pb, geob_data->data, taglen)) < taglen) + av_log(s, AV_LOG_WARNING, "Error reading GEOB frame, data truncated.\n"); + geob_data->datasize = len; + } else { + geob_data->data = NULL; + geob_data->datasize = 0; + } + + /* add data to the list */ + new_extra->tag = "GEOB"; + new_extra->data = geob_data; + new_extra->next = *extra_meta; + *extra_meta = new_extra; + + return; + +fail: + av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", tag); + free_geobtag(geob_data); + av_free(new_extra); + return; +} + +static int is_number(const char *str) +{ + while (*str >= '0' && *str <= '9') str++; + return !*str; +} + +static AVDictionaryEntry* get_date_tag(AVDictionary *m, const char *tag) +{ + AVDictionaryEntry *t; + if ((t = av_dict_get(m, tag, NULL, AV_DICT_MATCH_CASE)) && + strlen(t->value) == 4 && is_number(t->value)) + return t; + return NULL; +} + +static void merge_date(AVDictionary **m) +{ + AVDictionaryEntry *t; + char date[17] = {0}; // YYYY-MM-DD hh:mm + + if (!(t = get_date_tag(*m, "TYER")) && + !(t = get_date_tag(*m, "TYE"))) + return; + av_strlcpy(date, t->value, 5); + av_dict_set(m, "TYER", NULL, 0); + av_dict_set(m, "TYE", NULL, 0); + + if (!(t = get_date_tag(*m, "TDAT")) && + !(t = get_date_tag(*m, "TDA"))) + goto finish; + snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value); + av_dict_set(m, "TDAT", NULL, 0); + av_dict_set(m, "TDA", NULL, 0); + + if (!(t = get_date_tag(*m, "TIME")) && + !(t = get_date_tag(*m, "TIM"))) + goto finish; + snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2); + av_dict_set(m, "TIME", NULL, 0); + av_dict_set(m, "TIM", NULL, 0); + +finish: + if (date[0]) + av_dict_set(m, "date", date, 0); +} + +static void free_apic(void *obj) +{ + ID3v2ExtraMetaAPIC *apic = obj; + av_buffer_unref(&apic->buf); + av_freep(&apic->description); + av_freep(&apic); +} + +static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta) +{ + int enc, pic_type; + char mimetype[64]; + const CodecMime *mime = ff_id3v2_mime_tags; + enum AVCodecID id = AV_CODEC_ID_NONE; + ID3v2ExtraMetaAPIC *apic = NULL; + ID3v2ExtraMeta *new_extra = NULL; + int64_t end = avio_tell(pb) + taglen; + + if (taglen <= 4) + goto fail; + + new_extra = av_mallocz(sizeof(*new_extra)); + apic = av_mallocz(sizeof(*apic)); + if (!new_extra || !apic) + goto fail; + + enc = avio_r8(pb); + taglen--; + + /* mimetype */ + taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype)); + while (mime->id != AV_CODEC_ID_NONE) { + if (!av_strncasecmp(mime->str, mimetype, sizeof(mimetype))) { + id = mime->id; + break; + } + mime++; + } + if (id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_WARNING, "Unknown attached picture mimetype: %s, skipping.\n", mimetype); + goto fail; + } + apic->id = id; + + /* picture type */ + pic_type = avio_r8(pb); + taglen--; + if (pic_type < 0 || pic_type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) { + av_log(s, AV_LOG_WARNING, "Unknown attached picture type %d.\n", pic_type); + pic_type = 0; + } + apic->type = ff_id3v2_picture_types[pic_type]; + + /* description and picture data */ + if (decode_str(s, pb, enc, &apic->description, &taglen) < 0) { + av_log(s, AV_LOG_ERROR, "Error decoding attached picture description.\n"); + goto fail; + } + + apic->buf = av_buffer_alloc(taglen + FF_INPUT_BUFFER_PADDING_SIZE); + if (!apic->buf || !taglen || avio_read(pb, apic->buf->data, taglen) != taglen) + goto fail; + memset(apic->buf->data + taglen, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + new_extra->tag = "APIC"; + new_extra->data = apic; + new_extra->next = *extra_meta; + *extra_meta = new_extra; + + return; + +fail: + if (apic) + free_apic(apic); + av_freep(&new_extra); + avio_seek(pb, end, SEEK_SET); +} + +static void read_chapter(AVFormatContext *s, AVIOContext *pb, int taglen, char *tag, ID3v2ExtraMeta **extra_meta) +{ + AVRational time_base = {1, 1000}; + char title[1024]; + uint32_t start, end; + + taglen -= avio_get_str(pb, taglen, title, sizeof(title)); + if (taglen < 16) + return; + + start = avio_rb32(pb); + end = avio_rb32(pb); + taglen -= 27; + if (taglen > 0) { + char tag[4]; + + avio_skip(pb, 8); + avio_read(pb, tag, 4); + if (!memcmp(tag, "TIT2", 4)) { + taglen = FFMIN(taglen, avio_rb32(pb)); + if (taglen < 0) + return; + avio_skip(pb, 3); + avio_get_str(pb, taglen, title, sizeof(title)); + } + } + + avpriv_new_chapter(s, s->nb_chapters + 1, time_base, start, end, title); +} + +typedef struct ID3v2EMFunc { + const char *tag3; + const char *tag4; + void (*read)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta **); + void (*free)(void *obj); +} ID3v2EMFunc; + +static const ID3v2EMFunc id3v2_extra_meta_funcs[] = { + { "GEO", "GEOB", read_geobtag, free_geobtag }, + { "PIC", "APIC", read_apic, free_apic }, + { "CHAP","CHAP", read_chapter, NULL }, + { NULL } +}; + +/** + * Get the corresponding ID3v2EMFunc struct for a tag. + * @param isv34 Determines if v2.2 or v2.3/4 strings are used + * @return A pointer to the ID3v2EMFunc struct if found, NULL otherwise. + */ +static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34) +{ + int i = 0; + while (id3v2_extra_meta_funcs[i].tag3) { + if (tag && !memcmp(tag, + (isv34 ? id3v2_extra_meta_funcs[i].tag4 : + id3v2_extra_meta_funcs[i].tag3), + (isv34 ? 4 : 3))) + return &id3v2_extra_meta_funcs[i]; + i++; + } + return NULL; +} + +static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags, ID3v2ExtraMeta **extra_meta) +{ + int isv34, unsync; + unsigned tlen; + char tag[5]; + int64_t next, end = avio_tell(s->pb) + len; + int taghdrlen; + const char *reason = NULL; + AVIOContext pb; + AVIOContext *pbx; + unsigned char *buffer = NULL; + int buffer_size = 0; + const ID3v2EMFunc *extra_func = NULL; + unsigned char *uncompressed_buffer = NULL; + int uncompressed_buffer_size = 0; + + av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len); + + switch (version) { + case 2: + if (flags & 0x40) { + reason = "compression"; + goto error; + } + isv34 = 0; + taghdrlen = 6; + break; + + case 3: + case 4: + isv34 = 1; + taghdrlen = 10; + break; + + default: + reason = "version"; + goto error; + } + + unsync = flags & 0x80; + + if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */ + int extlen = get_size(s->pb, 4); + if (version == 4) + extlen -= 4; // in v2.4 the length includes the length field we just read + + if (extlen < 0) { + reason = "invalid extended header length"; + goto error; + } + avio_skip(s->pb, extlen); + len -= extlen + 4; + if (len < 0) { + reason = "extended header too long."; + goto error; + } + } + + while (len >= taghdrlen) { + unsigned int tflags = 0; + int tunsync = 0; + int tcomp = 0; + int tencr = 0; + unsigned long dlen; + + if (isv34) { + avio_read(s->pb, tag, 4); + tag[4] = 0; + if(version==3){ + tlen = avio_rb32(s->pb); + }else + tlen = get_size(s->pb, 4); + tflags = avio_rb16(s->pb); + tunsync = tflags & ID3v2_FLAG_UNSYNCH; + } else { + avio_read(s->pb, tag, 3); + tag[3] = 0; + tlen = avio_rb24(s->pb); + } + if (tlen > (1<<28)) + break; + len -= taghdrlen + tlen; + + if (len < 0) + break; + + next = avio_tell(s->pb) + tlen; + + if (!tlen) { + if (tag[0]) + av_log(s, AV_LOG_DEBUG, "Invalid empty frame %s, skipping.\n", tag); + continue; + } + + if (tflags & ID3v2_FLAG_DATALEN) { + if (tlen < 4) + break; + dlen = avio_rb32(s->pb); + tlen -= 4; + } else + dlen = tlen; + + tcomp = tflags & ID3v2_FLAG_COMPRESSION; + tencr = tflags & ID3v2_FLAG_ENCRYPTION; + + /* skip encrypted tags and, if no zlib, compressed tags */ + if (tencr || (!CONFIG_ZLIB && tcomp)) { + const char *type; + if (!tcomp) + type = "encrypted"; + else if (!tencr) + type = "compressed"; + else + type = "encrypted and compressed"; + + av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag); + avio_skip(s->pb, tlen); + /* check for text tag or supported special meta tag */ + } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) { + pbx = s->pb; + + if (unsync || tunsync || tcomp) { + av_fast_malloc(&buffer, &buffer_size, tlen); + if (!buffer) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %d bytes\n", tlen); + goto seek; + } + } + if (unsync || tunsync) { + int64_t end = avio_tell(s->pb) + tlen; + uint8_t *b; + + b = buffer; + while (avio_tell(s->pb) < end && b - buffer < tlen) { + *b++ = avio_r8(s->pb); + if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 && b - buffer < tlen) { + uint8_t val = avio_r8(s->pb); + *b++ = val ? val : avio_r8(s->pb); + } + } + ffio_init_context(&pb, buffer, b - buffer, 0, NULL, NULL, NULL, NULL); + tlen = b - buffer; + pbx = &pb; // read from sync buffer + } + +#if CONFIG_ZLIB + if (tcomp) { + int err; + + av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen); + + av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen); + if (!uncompressed_buffer) { + av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen); + goto seek; + } + + if (!(unsync || tunsync)) { + err = avio_read(s->pb, buffer, tlen); + if (err < 0) { + av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n"); + goto seek; + } + tlen = err; + } + + err = uncompress(uncompressed_buffer, &dlen, buffer, tlen); + if (err != Z_OK) { + av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err); + goto seek; + } + ffio_init_context(&pb, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL); + tlen = dlen; + pbx = &pb; // read from sync buffer + } +#endif + if (tag[0] == 'T') + /* parse text tag */ + read_ttag(s, pbx, tlen, tag); + else + /* parse special meta tag */ + extra_func->read(s, pbx, tlen, tag, extra_meta); + } + else if (!tag[0]) { + if (tag[1]) + av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n"); + avio_skip(s->pb, tlen); + break; + } + /* Skip to end of tag */ +seek: + avio_seek(s->pb, next, SEEK_SET); + } + + if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */ + end += 10; + + error: + if (reason) + av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason); + avio_seek(s->pb, end, SEEK_SET); + av_free(buffer); + av_free(uncompressed_buffer); + return; +} + +void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta) +{ + int len, ret; + uint8_t buf[ID3v2_HEADER_SIZE]; + int found_header; + int64_t off; + + do { + /* save the current offset in case there's nothing to read/skip */ + off = avio_tell(s->pb); + ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE); + if (ret != ID3v2_HEADER_SIZE) { + avio_seek(s->pb, off, SEEK_SET); + break; + } + found_header = ff_id3v2_match(buf, magic); + if (found_header) { + /* parse ID3v2 header */ + len = ((buf[6] & 0x7f) << 21) | + ((buf[7] & 0x7f) << 14) | + ((buf[8] & 0x7f) << 7) | + (buf[9] & 0x7f); + ff_id3v2_parse(s, len, buf[3], buf[5], extra_meta); + } else { + avio_seek(s->pb, off, SEEK_SET); + } + } while (found_header); + ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv); + ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv); + ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv); + merge_date(&s->metadata); +} + +void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) +{ + ID3v2ExtraMeta *current = *extra_meta, *next; + const ID3v2EMFunc *extra_func; + + while (current) { + if ((extra_func = get_extra_meta_func(current->tag, 1))) + extra_func->free(current->data); + next = current->next; + av_freep(¤t); + current = next; + } +} + +int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +{ + ID3v2ExtraMeta *cur; + + for (cur = *extra_meta; cur; cur = cur->next) { + ID3v2ExtraMetaAPIC *apic; + AVStream *st; + + if (strcmp(cur->tag, "APIC")) + continue; + apic = cur->data; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = apic->id; + av_dict_set(&st->metadata, "title", apic->description, 0); + av_dict_set(&st->metadata, "comment", apic->type, 0); + + av_init_packet(&st->attached_pic); + st->attached_pic.buf = apic->buf; + st->attached_pic.data = apic->buf->data; + st->attached_pic.size = apic->buf->size - FF_INPUT_BUFFER_PADDING_SIZE; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + + apic->buf = NULL; + } + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v2.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/id3v2.o libavformat/id3v2.o: libavformat/id3v2.c config.h \ + libavformat/id3v2.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/metadata.h libavformat/id3v1.h libavutil/avstring.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavformat/avio_internal.h \ + libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v2.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,163 @@ +/* + * ID3v2 header parser + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_ID3V2_H +#define AVFORMAT_ID3V2_H + +#include +#include "avformat.h" +#include "internal.h" +#include "metadata.h" + +#define ID3v2_HEADER_SIZE 10 + +/** + * Default magic bytes for ID3v2 header: "ID3" + */ +#define ID3v2_DEFAULT_MAGIC "ID3" + +#define ID3v2_FLAG_DATALEN 0x0001 +#define ID3v2_FLAG_UNSYNCH 0x0002 +#define ID3v2_FLAG_ENCRYPTION 0x0004 +#define ID3v2_FLAG_COMPRESSION 0x0008 + +enum ID3v2Encoding { + ID3v2_ENCODING_ISO8859 = 0, + ID3v2_ENCODING_UTF16BOM = 1, + ID3v2_ENCODING_UTF16BE = 2, + ID3v2_ENCODING_UTF8 = 3, +}; + +typedef struct ID3v2EncContext { + int version; ///< ID3v2 minor version, either 3 or 4 + int64_t size_pos; ///< offset of the tag total size + int len; ///< size of the tag written so far +} ID3v2EncContext; + +typedef struct ID3v2ExtraMeta { + const char *tag; + void *data; + struct ID3v2ExtraMeta *next; +} ID3v2ExtraMeta; + +typedef struct ID3v2ExtraMetaGEOB { + uint32_t datasize; + uint8_t *mime_type; + uint8_t *file_name; + uint8_t *description; + uint8_t *data; +} ID3v2ExtraMetaGEOB; + +typedef struct ID3v2ExtraMetaAPIC { + AVBufferRef *buf; + const char *type; + uint8_t *description; + enum AVCodecID id; +} ID3v2ExtraMetaAPIC; + +/** + * Detect ID3v2 Header. + * @param buf must be ID3v2_HEADER_SIZE byte long + * @param magic magic bytes to identify the header. + * If in doubt, use ID3v2_DEFAULT_MAGIC. + */ +int ff_id3v2_match(const uint8_t *buf, const char *magic); + +/** + * Get the length of an ID3v2 tag. + * @param buf must be ID3v2_HEADER_SIZE bytes long and point to the start of an + * already detected ID3v2 tag + */ +int ff_id3v2_tag_len(const uint8_t *buf); + +/** + * Read an ID3v2 tag, including supported extra metadata + * @param extra_meta If not NULL, extra metadata is parsed into a list of + * ID3v2ExtraMeta structs and *extra_meta points to the head of the list + */ +void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra_meta); + +/** + * Initialize an ID3v2 tag. + */ +void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, + const char *magic); + +/** + * Convert and write all global metadata from s into an ID3v2 tag. + */ +int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3); + +/** + * Write an attached picture from pkt into an ID3v2 tag. + */ +int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt); + +/** + * Finalize an opened ID3v2 tag. + */ +void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb); + +/** + * Write an ID3v2 tag containing all global metadata from s. + * @param id3v2_version Subversion of ID3v2; supported values are 3 and 4 + * @param magic magic bytes to identify the header + * If in doubt, use ID3v2_DEFAULT_MAGIC. + */ +int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version, const char *magic); + +/** + * Free memory allocated parsing special (non-text) metadata. + * @param extra_meta Pointer to a pointer to the head of a ID3v2ExtraMeta list, *extra_meta is set to NULL. + */ +void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta); + +/** + * Create a stream for each APIC (attached picture) extracted from the + * ID3v2 header. + */ +int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); + +extern const AVMetadataConv ff_id3v2_34_metadata_conv[]; +extern const AVMetadataConv ff_id3v2_4_metadata_conv[]; + +/** + * A list of text information frames allowed in both ID3 v2.3 and v2.4 + * http://www.id3.org/id3v2.4.0-frames + * http://www.id3.org/id3v2.4.0-changes + */ +extern const char ff_id3v2_tags[][4]; + +/** + * ID3v2.4-only text information frames. + */ +extern const char ff_id3v2_4_tags[][4]; + +/** + * ID3v2.3-only text information frames. + */ +extern const char ff_id3v2_3_tags[][4]; + +extern const CodecMime ff_id3v2_mime_tags[]; + +extern const char *ff_id3v2_picture_types[21]; + +#endif /* AVFORMAT_ID3V2_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2.o Binary file ffmpeg/libavformat/id3v2.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2enc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v2enc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,284 @@ +/* + * ID3v2 header writer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio.h" +#include "id3v2.h" + +static void id3v2_put_size(AVIOContext *pb, int size) +{ + avio_w8(pb, size >> 21 & 0x7f); + avio_w8(pb, size >> 14 & 0x7f); + avio_w8(pb, size >> 7 & 0x7f); + avio_w8(pb, size & 0x7f); +} + +static int string_is_ascii(const uint8_t *str) +{ + while (*str && *str < 128) str++; + return !*str; +} + +static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str, + enum ID3v2Encoding enc) +{ + int (*put)(AVIOContext*, const char*); + + if (enc == ID3v2_ENCODING_UTF16BOM) { + avio_wl16(pb, 0xFEFF); /* BOM */ + put = avio_put_str16le; + } else + put = avio_put_str; + + put(pb, str); +} + +/** + * Write a text frame with one (normal frames) or two (TXXX frames) strings + * according to encoding (only UTF-8 or UTF-16+BOM supported). + * @return number of bytes written or a negative error code. + */ +static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2, + uint32_t tag, enum ID3v2Encoding enc) +{ + int len; + uint8_t *pb; + AVIOContext *dyn_buf; + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + /* check if the strings are ASCII-only and use UTF16 only if + * they're not */ + if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) && + (!str2 || string_is_ascii(str2))) + enc = ID3v2_ENCODING_ISO8859; + + avio_w8(dyn_buf, enc); + id3v2_encode_string(dyn_buf, str1, enc); + if (str2) + id3v2_encode_string(dyn_buf, str2, enc); + len = avio_close_dyn_buf(dyn_buf, &pb); + + avio_wb32(avioc, tag); + /* ID3v2.3 frame size is not synchsafe */ + if (id3->version == 3) + avio_wb32(avioc, len); + else + id3v2_put_size(avioc, len); + avio_wb16(avioc, 0); + avio_write(avioc, pb, len); + + av_freep(&pb); + return len + ID3v2_HEADER_SIZE; +} + +static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t, + const char table[][4], enum ID3v2Encoding enc) +{ + uint32_t tag; + int i; + + if (t->key[0] != 'T' || strlen(t->key) != 4) + return -1; + tag = AV_RB32(t->key); + for (i = 0; *table[i]; i++) + if (tag == AV_RB32(table[i])) + return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc); + return -1; +} + +static void id3v2_3_metadata_split_date(AVDictionary **pm) +{ + AVDictionaryEntry *mtag = NULL; + AVDictionary *dst = NULL; + const char *key, *value; + char year[5] = {0}, day_month[5] = {0}; + int i; + + while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) { + key = mtag->key; + if (!av_strcasecmp(key, "date")) { + /* split date tag using "YYYY-MM-DD" format into year and month/day segments */ + value = mtag->value; + i = 0; + while (value[i] >= '0' && value[i] <= '9') i++; + if (value[i] == '\0' || value[i] == '-') { + av_strlcpy(year, value, sizeof(year)); + av_dict_set(&dst, "TYER", year, 0); + + if (value[i] == '-' && + value[i+1] >= '0' && value[i+1] <= '1' && + value[i+2] >= '0' && value[i+2] <= '9' && + value[i+3] == '-' && + value[i+4] >= '0' && value[i+4] <= '3' && + value[i+5] >= '0' && value[i+5] <= '9' && + (value[i+6] == '\0' || value[i+6] == ' ')) { + snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1); + av_dict_set(&dst, "TDAT", day_month, 0); + } + } else + av_dict_set(&dst, key, value, 0); + } else + av_dict_set(&dst, key, mtag->value, 0); + } + av_dict_free(pm); + *pm = dst; +} + +void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, + const char *magic) +{ + id3->version = id3v2_version; + + avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version)); + avio_w8(pb, 0); + avio_w8(pb, 0); /* flags */ + + /* reserve space for size */ + id3->size_pos = avio_tell(pb); + avio_wb32(pb, 0); +} + +int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) +{ + AVDictionaryEntry *t = NULL; + int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : + ID3v2_ENCODING_UTF8; + + ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); + if (id3->version == 3) + id3v2_3_metadata_split_date(&s->metadata); + else if (id3->version == 4) + ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); + + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { + int ret; + + if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) { + id3->len += ret; + continue; + } + if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ? + ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { + id3->len += ret; + continue; + } + + /* unknown tag, write as TXXX frame */ + if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) + return ret; + id3->len += ret; + } + + return 0; +} + +int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + AVDictionaryEntry *e; + + AVIOContext *dyn_buf; + uint8_t *buf; + const CodecMime *mime = ff_id3v2_mime_tags; + const char *mimetype = NULL, *desc = ""; + int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : + ID3v2_ENCODING_UTF8; + int i, len, type = 0; + + /* get the mimetype*/ + while (mime->id != AV_CODEC_ID_NONE) { + if (mime->id == st->codec->codec_id) { + mimetype = mime->str; + break; + } + mime++; + } + if (!mimetype) { + av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot " + "write an attached picture.\n", st->index); + return AVERROR(EINVAL); + } + + /* get the picture type */ + e = av_dict_get(st->metadata, "comment", NULL, 0); + for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) { + if (strstr(ff_id3v2_picture_types[i], e->value) == ff_id3v2_picture_types[i]) { + type = i; + break; + } + } + + /* get the description */ + if ((e = av_dict_get(st->metadata, "title", NULL, 0))) + desc = e->value; + + /* start writing */ + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + avio_w8(dyn_buf, enc); + avio_put_str(dyn_buf, mimetype); + avio_w8(dyn_buf, type); + id3v2_encode_string(dyn_buf, desc, enc); + avio_write(dyn_buf, pkt->data, pkt->size); + len = avio_close_dyn_buf(dyn_buf, &buf); + + avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C')); + if (id3->version == 3) + avio_wb32(s->pb, len); + else + id3v2_put_size(s->pb, len); + avio_wb16(s->pb, 0); + avio_write(s->pb, buf, len); + av_freep(&buf); + + id3->len += len + ID3v2_HEADER_SIZE; + + return 0; +} + +void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb) +{ + int64_t cur_pos = avio_tell(pb); + avio_seek(pb, id3->size_pos, SEEK_SET); + id3v2_put_size(pb, id3->len); + avio_seek(pb, cur_pos, SEEK_SET); +} + +int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version, + const char *magic) +{ + ID3v2EncContext id3 = { 0 }; + int ret; + + ff_id3v2_start(&id3, s->pb, id3v2_version, magic); + if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0) + return ret; + ff_id3v2_finish(&id3, s->pb); + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2enc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/id3v2enc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/id3v2enc.o libavformat/id3v2enc.o: libavformat/id3v2enc.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/dict.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/bswap.h \ + config.h libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/id3v2.h libavformat/internal.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/id3v2enc.o Binary file ffmpeg/libavformat/id3v2enc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idcin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idcin.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,380 @@ +/* + * id Quake II CIN File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * id Quake II CIN file demuxer by Mike Melanson (melanson@pcisys.net) + * For more information about the id CIN format, visit: + * http://www.csse.monash.edu.au/~timf/ + * + * CIN is a somewhat quirky and ill-defined format. Here are some notes + * for anyone trying to understand the technical details of this format: + * + * The format has no definite file signature. This is problematic for a + * general-purpose media player that wants to automatically detect file + * types. However, a CIN file does start with 5 32-bit numbers that + * specify audio and video parameters. This demuxer gets around the lack + * of file signature by performing sanity checks on those parameters. + * Probabalistically, this is a reasonable solution since the number of + * valid combinations of the 5 parameters is a very small subset of the + * total 160-bit number space. + * + * Refer to the function idcin_probe() for the precise A/V parameters + * that this demuxer allows. + * + * Next, each audio and video frame has a duration of 1/14 sec. If the + * audio sample rate is a multiple of the common frequency 22050 Hz it will + * divide evenly by 14. However, if the sample rate is 11025 Hz: + * 11025 (samples/sec) / 14 (frames/sec) = 787.5 (samples/frame) + * The way the CIN stores audio in this case is by storing 787 sample + * frames in the first audio frame and 788 sample frames in the second + * audio frame. Therefore, the total number of bytes in an audio frame + * is given as: + * audio frame #0: 787 * (bytes/sample) * (# channels) bytes in frame + * audio frame #1: 788 * (bytes/sample) * (# channels) bytes in frame + * audio frame #2: 787 * (bytes/sample) * (# channels) bytes in frame + * audio frame #3: 788 * (bytes/sample) * (# channels) bytes in frame + * + * Finally, not all id CIN creation tools agree on the resolution of the + * color palette, apparently. Some creation tools specify red, green, and + * blue palette components in terms of 6-bit VGA color DAC values which + * range from 0..63. Other tools specify the RGB components as full 8-bit + * values that range from 0..255. Since there are no markers in the file to + * differentiate between the two variants, this demuxer uses the following + * heuristic: + * - load the 768 palette bytes from disk + * - assume that they will need to be shifted left by 2 bits to + * transform them from 6-bit values to 8-bit values + * - scan through all 768 palette bytes + * - if any bytes exceed 63, do not shift the bytes at all before + * transmitting them to the video decoder + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define HUFFMAN_TABLE_SIZE (64 * 1024) +#define IDCIN_FPS 14 + +typedef struct IdcinDemuxContext { + int video_stream_index; + int audio_stream_index; + int audio_chunk_size1; + int audio_chunk_size2; + int block_align; + + /* demux state variables */ + int current_audio_chunk; + int next_chunk_is_video; + int audio_present; + int64_t first_pkt_pos; +} IdcinDemuxContext; + +static int idcin_probe(AVProbeData *p) +{ + unsigned int number; + + /* + * This is what you could call a "probabilistic" file check: id CIN + * files don't have a definite file signature. In lieu of such a marker, + * perform sanity checks on the 5 32-bit header fields: + * width, height: greater than 0, less than or equal to 1024 + * audio sample rate: greater than or equal to 8000, less than or + * equal to 48000, or 0 for no audio + * audio sample width (bytes/sample): 0 for no audio, or 1 or 2 + * audio channels: 0 for no audio, or 1 or 2 + */ + + /* check we have enough data to do all checks, otherwise the + 0-padding may cause a wrong recognition */ + if (p->buf_size < 20) + return 0; + + /* check the video width */ + number = AV_RL32(&p->buf[0]); + if ((number == 0) || (number > 1024)) + return 0; + + /* check the video height */ + number = AV_RL32(&p->buf[4]); + if ((number == 0) || (number > 1024)) + return 0; + + /* check the audio sample rate */ + number = AV_RL32(&p->buf[8]); + if ((number != 0) && ((number < 8000) | (number > 48000))) + return 0; + + /* check the audio bytes/sample */ + number = AV_RL32(&p->buf[12]); + if (number > 2) + return 0; + + /* check the audio channels */ + number = AV_RL32(&p->buf[16]); + if (number > 2) + return 0; + + /* return half certainly since this check is a bit sketchy */ + return AVPROBE_SCORE_MAX / 2; +} + +static int idcin_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + IdcinDemuxContext *idcin = s->priv_data; + AVStream *st; + unsigned int width, height; + unsigned int sample_rate, bytes_per_sample, channels; + int ret; + + /* get the 5 header parameters */ + width = avio_rl32(pb); + height = avio_rl32(pb); + sample_rate = avio_rl32(pb); + bytes_per_sample = avio_rl32(pb); + channels = avio_rl32(pb); + + if (s->pb->eof_reached) { + av_log(s, AV_LOG_ERROR, "incomplete header\n"); + return s->pb->error ? s->pb->error : AVERROR_EOF; + } + + if (av_image_check_size(width, height, 0, s) < 0) + return AVERROR_INVALIDDATA; + if (sample_rate > 0) { + if (sample_rate < 14 || sample_rate > INT_MAX) { + av_log(s, AV_LOG_ERROR, "invalid sample rate: %u\n", sample_rate); + return AVERROR_INVALIDDATA; + } + if (bytes_per_sample < 1 || bytes_per_sample > 2) { + av_log(s, AV_LOG_ERROR, "invalid bytes per sample: %u\n", + bytes_per_sample); + return AVERROR_INVALIDDATA; + } + if (channels < 1 || channels > 2) { + av_log(s, AV_LOG_ERROR, "invalid channels: %u\n", channels); + return AVERROR_INVALIDDATA; + } + idcin->audio_present = 1; + } else { + /* if sample rate is 0, assume no audio */ + idcin->audio_present = 0; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 33, 1, IDCIN_FPS); + st->start_time = 0; + idcin->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_IDCIN; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = width; + st->codec->height = height; + + /* load up the Huffman tables into extradata */ + st->codec->extradata_size = HUFFMAN_TABLE_SIZE; + st->codec->extradata = av_malloc(HUFFMAN_TABLE_SIZE); + ret = avio_read(pb, st->codec->extradata, HUFFMAN_TABLE_SIZE); + if (ret < 0) { + return ret; + } else if (ret != HUFFMAN_TABLE_SIZE) { + av_log(s, AV_LOG_ERROR, "incomplete header\n"); + return AVERROR(EIO); + } + + if (idcin->audio_present) { + idcin->audio_present = 1; + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 63, 1, sample_rate); + st->start_time = 0; + idcin->audio_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 1; + st->codec->channels = channels; + st->codec->channel_layout = channels > 1 ? AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; + st->codec->sample_rate = sample_rate; + st->codec->bits_per_coded_sample = bytes_per_sample * 8; + st->codec->bit_rate = sample_rate * bytes_per_sample * 8 * channels; + st->codec->block_align = idcin->block_align = bytes_per_sample * channels; + if (bytes_per_sample == 1) + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + else + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + + if (sample_rate % 14 != 0) { + idcin->audio_chunk_size1 = (sample_rate / 14) * + bytes_per_sample * channels; + idcin->audio_chunk_size2 = (sample_rate / 14 + 1) * + bytes_per_sample * channels; + } else { + idcin->audio_chunk_size1 = idcin->audio_chunk_size2 = + (sample_rate / 14) * bytes_per_sample * channels; + } + idcin->current_audio_chunk = 0; + } + + idcin->next_chunk_is_video = 1; + idcin->first_pkt_pos = avio_tell(s->pb); + + return 0; +} + +static int idcin_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + int ret; + unsigned int command; + unsigned int chunk_size; + IdcinDemuxContext *idcin = s->priv_data; + AVIOContext *pb = s->pb; + int i; + int palette_scale; + unsigned char r, g, b; + unsigned char palette_buffer[768]; + uint32_t palette[256]; + + if (url_feof(s->pb)) + return s->pb->error ? s->pb->error : AVERROR_EOF; + + if (idcin->next_chunk_is_video) { + command = avio_rl32(pb); + if (command == 2) { + return AVERROR(EIO); + } else if (command == 1) { + /* trigger a palette change */ + ret = avio_read(pb, palette_buffer, 768); + if (ret < 0) { + return ret; + } else if (ret != 768) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + return AVERROR(EIO); + } + /* scale the palette as necessary */ + palette_scale = 2; + for (i = 0; i < 768; i++) + if (palette_buffer[i] > 63) { + palette_scale = 0; + break; + } + + for (i = 0; i < 256; i++) { + r = palette_buffer[i * 3 ] << palette_scale; + g = palette_buffer[i * 3 + 1] << palette_scale; + b = palette_buffer[i * 3 + 2] << palette_scale; + palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); + if (palette_scale == 2) + palette[i] |= palette[i] >> 6 & 0x30303; + } + } + + if (s->pb->eof_reached) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + return s->pb->error ? s->pb->error : AVERROR_EOF; + } + chunk_size = avio_rl32(pb); + if (chunk_size < 4 || chunk_size > INT_MAX - 4) { + av_log(s, AV_LOG_ERROR, "invalid chunk size: %u\n", chunk_size); + return AVERROR_INVALIDDATA; + } + /* skip the number of decoded bytes (always equal to width * height) */ + avio_skip(pb, 4); + if (chunk_size < 4) + return AVERROR_INVALIDDATA; + chunk_size -= 4; + ret= av_get_packet(pb, pkt, chunk_size); + if (ret < 0) + return ret; + else if (ret != chunk_size) { + av_log(s, AV_LOG_ERROR, "incomplete packet\n"); + av_free_packet(pkt); + return AVERROR(EIO); + } + if (command == 1) { + uint8_t *pal; + + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, + AVPALETTE_SIZE); + if (!pal) { + av_free_packet(pkt); + return AVERROR(ENOMEM); + } + memcpy(pal, palette, AVPALETTE_SIZE); + pkt->flags |= AV_PKT_FLAG_KEY; + } + pkt->stream_index = idcin->video_stream_index; + pkt->duration = 1; + } else { + /* send out the audio chunk */ + if (idcin->current_audio_chunk) + chunk_size = idcin->audio_chunk_size2; + else + chunk_size = idcin->audio_chunk_size1; + ret= av_get_packet(pb, pkt, chunk_size); + if (ret < 0) + return ret; + pkt->stream_index = idcin->audio_stream_index; + pkt->duration = chunk_size / idcin->block_align; + + idcin->current_audio_chunk ^= 1; + } + + if (idcin->audio_present) + idcin->next_chunk_is_video ^= 1; + + return 0; +} + +static int idcin_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + IdcinDemuxContext *idcin = s->priv_data; + + if (idcin->first_pkt_pos > 0) { + int ret = avio_seek(s->pb, idcin->first_pkt_pos, SEEK_SET); + if (ret < 0) + return ret; + ff_update_cur_dts(s, s->streams[idcin->video_stream_index], 0); + idcin->next_chunk_is_video = 1; + idcin->current_audio_chunk = 0; + return 0; + } + return -1; +} + +AVInputFormat ff_idcin_demuxer = { + .name = "idcin", + .long_name = NULL_IF_CONFIG_SMALL("id Cinematic"), + .priv_data_size = sizeof(IdcinDemuxContext), + .read_probe = idcin_probe, + .read_header = idcin_read_header, + .read_packet = idcin_read_packet, + .read_seek = idcin_read_seek, + .flags = AVFMT_NO_BYTE_SEEK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idcin.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idcin.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/idcin.o libavformat/idcin.o: libavformat/idcin.c \ + libavutil/channel_layout.h libavutil/imgutils.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/pixdesc.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idcin.o Binary file ffmpeg/libavformat/idcin.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idroqdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,237 @@ +/* + * id RoQ (.roq) File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * id RoQ format file demuxer + * by Mike Melanson (melanson@pcisys.net) + * for more information on the .roq file format, visit: + * http://www.csse.monash.edu.au/~timf/ + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" + +#define RoQ_MAGIC_NUMBER 0x1084 +#define RoQ_CHUNK_PREAMBLE_SIZE 8 +#define RoQ_AUDIO_SAMPLE_RATE 22050 +#define RoQ_CHUNKS_TO_SCAN 30 + +#define RoQ_INFO 0x1001 +#define RoQ_QUAD_CODEBOOK 0x1002 +#define RoQ_QUAD_VQ 0x1011 +#define RoQ_SOUND_MONO 0x1020 +#define RoQ_SOUND_STEREO 0x1021 + +typedef struct RoqDemuxContext { + + int frame_rate; + int width; + int height; + int audio_channels; + + int video_stream_index; + int audio_stream_index; + + int64_t video_pts; + unsigned int audio_frame_count; + +} RoqDemuxContext; + +static int roq_probe(AVProbeData *p) +{ + if ((AV_RL16(&p->buf[0]) != RoQ_MAGIC_NUMBER) || + (AV_RL32(&p->buf[2]) != 0xFFFFFFFF)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int roq_read_header(AVFormatContext *s) +{ + RoqDemuxContext *roq = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE]; + + /* get the main header */ + if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR(EIO); + roq->frame_rate = AV_RL16(&preamble[6]); + + /* init private context parameters */ + roq->width = roq->height = roq->audio_channels = roq->video_pts = + roq->audio_frame_count = 0; + roq->audio_stream_index = -1; + roq->video_stream_index = -1; + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + return 0; +} + +static int roq_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + RoqDemuxContext *roq = s->priv_data; + AVIOContext *pb = s->pb; + int ret = 0; + unsigned int chunk_size; + unsigned int chunk_type; + unsigned int codebook_size; + unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE]; + int packet_read = 0; + int64_t codebook_offset; + + while (!packet_read) { + + if (url_feof(s->pb)) + return AVERROR(EIO); + + /* get the next chunk preamble */ + if ((ret = avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR(EIO); + + chunk_type = AV_RL16(&preamble[0]); + chunk_size = AV_RL32(&preamble[2]); + if(chunk_size > INT_MAX) + return AVERROR_INVALIDDATA; + + chunk_size = ffio_limit(pb, chunk_size); + + switch (chunk_type) { + + case RoQ_INFO: + if (roq->video_stream_index == -1) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 63, 1, roq->frame_rate); + roq->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_ROQ; + st->codec->codec_tag = 0; /* no fourcc */ + + if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR(EIO); + st->codec->width = roq->width = AV_RL16(preamble); + st->codec->height = roq->height = AV_RL16(preamble + 2); + break; + } + /* don't care about this chunk anymore */ + avio_skip(pb, RoQ_CHUNK_PREAMBLE_SIZE); + break; + + case RoQ_QUAD_CODEBOOK: + /* packet needs to contain both this codebook and next VQ chunk */ + codebook_offset = avio_tell(pb) - RoQ_CHUNK_PREAMBLE_SIZE; + codebook_size = chunk_size; + avio_skip(pb, codebook_size); + if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != + RoQ_CHUNK_PREAMBLE_SIZE) + return AVERROR(EIO); + chunk_size = AV_RL32(&preamble[2]) + RoQ_CHUNK_PREAMBLE_SIZE * 2 + + codebook_size; + + /* rewind */ + avio_seek(pb, codebook_offset, SEEK_SET); + + /* load up the packet */ + ret= av_get_packet(pb, pkt, chunk_size); + if (ret != chunk_size) + return AVERROR(EIO); + pkt->stream_index = roq->video_stream_index; + pkt->pts = roq->video_pts++; + + packet_read = 1; + break; + + case RoQ_SOUND_MONO: + case RoQ_SOUND_STEREO: + if (roq->audio_stream_index == -1) { + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 32, 1, RoQ_AUDIO_SAMPLE_RATE); + roq->audio_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ROQ_DPCM; + st->codec->codec_tag = 0; /* no tag */ + if (chunk_type == RoQ_SOUND_STEREO) { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + roq->audio_channels = st->codec->channels; + st->codec->sample_rate = RoQ_AUDIO_SAMPLE_RATE; + st->codec->bits_per_coded_sample = 16; + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * + st->codec->bits_per_coded_sample; + st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; + } + case RoQ_QUAD_VQ: + /* load up the packet */ + if (av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE)) + return AVERROR(EIO); + /* copy over preamble */ + memcpy(pkt->data, preamble, RoQ_CHUNK_PREAMBLE_SIZE); + + if (chunk_type == RoQ_QUAD_VQ) { + pkt->stream_index = roq->video_stream_index; + pkt->pts = roq->video_pts++; + } else { + pkt->stream_index = roq->audio_stream_index; + pkt->pts = roq->audio_frame_count; + roq->audio_frame_count += (chunk_size / roq->audio_channels); + } + + pkt->pos= avio_tell(pb); + ret = avio_read(pb, pkt->data + RoQ_CHUNK_PREAMBLE_SIZE, + chunk_size); + if (ret != chunk_size) + ret = AVERROR(EIO); + + packet_read = 1; + break; + + default: + av_log(s, AV_LOG_ERROR, " unknown RoQ chunk (%04X)\n", chunk_type); + return AVERROR_INVALIDDATA; + } + } + + return ret; +} + +AVInputFormat ff_roq_demuxer = { + .name = "roq", + .long_name = NULL_IF_CONFIG_SMALL("id RoQ"), + .priv_data_size = sizeof(RoqDemuxContext), + .read_probe = roq_probe, + .read_header = roq_read_header, + .read_packet = roq_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idroqdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/idroqdec.o libavformat/idroqdec.o: libavformat/idroqdec.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqdec.o Binary file ffmpeg/libavformat/idroqdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idroqenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,46 @@ +/* + * id RoQ (.roq) File muxer + * Copyright (c) 2007 Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + + +static int roq_write_header(struct AVFormatContext *s) +{ + static const uint8_t header[] = { + 0x84, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x00 + }; + + avio_write(s->pb, header, 8); + avio_flush(s->pb); + + return 0; +} + +AVOutputFormat ff_roq_muxer = { + .name = "roq", + .long_name = NULL_IF_CONFIG_SMALL("raw id RoQ"), + .extensions = "roq", + .audio_codec = AV_CODEC_ID_ROQ_DPCM, + .video_codec = AV_CODEC_ID_ROQ, + .write_header = roq_write_header, + .write_packet = ff_raw_write_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/idroqenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/idroqenc.o libavformat/idroqenc.o: libavformat/idroqenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/idroqenc.o Binary file ffmpeg/libavformat/idroqenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iff.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iff.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2008 Jaikrishnan Menon + * Copyright (c) 2010 Peter Ross + * Copyright (c) 2010 Sebastian Vater + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * IFF file demuxer + * by Jaikrishnan Menon + * for more information on the .iff file format, visit: + * http://wiki.multimedia.cx/index.php?title=IFF + */ + +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" + +#define ID_8SVX MKTAG('8','S','V','X') +#define ID_16SV MKTAG('1','6','S','V') +#define ID_MAUD MKTAG('M','A','U','D') +#define ID_MHDR MKTAG('M','H','D','R') +#define ID_MDAT MKTAG('M','D','A','T') +#define ID_VHDR MKTAG('V','H','D','R') +#define ID_ATAK MKTAG('A','T','A','K') +#define ID_RLSE MKTAG('R','L','S','E') +#define ID_CHAN MKTAG('C','H','A','N') +#define ID_PBM MKTAG('P','B','M',' ') +#define ID_ILBM MKTAG('I','L','B','M') +#define ID_BMHD MKTAG('B','M','H','D') +#define ID_DGBL MKTAG('D','G','B','L') +#define ID_CAMG MKTAG('C','A','M','G') +#define ID_CMAP MKTAG('C','M','A','P') +#define ID_ACBM MKTAG('A','C','B','M') +#define ID_DEEP MKTAG('D','E','E','P') +#define ID_RGB8 MKTAG('R','G','B','8') +#define ID_RGBN MKTAG('R','G','B','N') + +#define ID_FORM MKTAG('F','O','R','M') +#define ID_ANNO MKTAG('A','N','N','O') +#define ID_AUTH MKTAG('A','U','T','H') +#define ID_CHRS MKTAG('C','H','R','S') +#define ID_COPYRIGHT MKTAG('(','c',')',' ') +#define ID_CSET MKTAG('C','S','E','T') +#define ID_FVER MKTAG('F','V','E','R') +#define ID_NAME MKTAG('N','A','M','E') +#define ID_TEXT MKTAG('T','E','X','T') +#define ID_ABIT MKTAG('A','B','I','T') +#define ID_BODY MKTAG('B','O','D','Y') +#define ID_DBOD MKTAG('D','B','O','D') +#define ID_DPEL MKTAG('D','P','E','L') +#define ID_DLOC MKTAG('D','L','O','C') +#define ID_TVDC MKTAG('T','V','D','C') + +#define LEFT 2 +#define RIGHT 4 +#define STEREO 6 + +/** + * This number of bytes if added at the beginning of each AVPacket + * which contain additional information about video properties + * which has to be shared between demuxer and decoder. + * This number may change between frames, e.g. the demuxer might + * set it to smallest possible size of 2 to indicate that there's + * no extradata changing in this frame. + */ +#define IFF_EXTRA_VIDEO_SIZE 41 + +typedef enum { + COMP_NONE, + COMP_FIB, + COMP_EXP +} svx8_compression_type; + +typedef struct { + int64_t body_pos; + int64_t body_end; + uint32_t body_size; + svx8_compression_type svx8_compression; + unsigned maud_bits; + unsigned maud_compression; + unsigned bitmap_compression; ///< delta compression method used + unsigned bpp; ///< bits per plane to decode (differs from bits_per_coded_sample if HAM) + unsigned ham; ///< 0 if non-HAM or number of hold bits (6 for bpp > 6, 4 otherwise) + unsigned flags; ///< 1 for EHB, 0 is no extra half darkening + unsigned transparency; ///< transparency color index in palette + unsigned masking; ///< masking method used + uint8_t tvdc[32]; ///< TVDC lookup table +} IffDemuxContext; + +/* Metadata string read */ +static int get_metadata(AVFormatContext *s, + const char *const tag, + const unsigned data_size) +{ + uint8_t *buf = ((data_size + 1) == 0) ? NULL : av_malloc(data_size + 1); + + if (!buf) + return AVERROR(ENOMEM); + + if (avio_read(s->pb, buf, data_size) < 0) { + av_free(buf); + return AVERROR(EIO); + } + buf[data_size] = 0; + av_dict_set(&s->metadata, tag, buf, AV_DICT_DONT_STRDUP_VAL); + return 0; +} + +static int iff_probe(AVProbeData *p) +{ + const uint8_t *d = p->buf; + + if ( AV_RL32(d) == ID_FORM && + (AV_RL32(d+8) == ID_8SVX || + AV_RL32(d+8) == ID_16SV || + AV_RL32(d+8) == ID_MAUD || + AV_RL32(d+8) == ID_PBM || + AV_RL32(d+8) == ID_ACBM || + AV_RL32(d+8) == ID_DEEP || + AV_RL32(d+8) == ID_ILBM || + AV_RL32(d+8) == ID_RGB8 || + AV_RL32(d+8) == ID_RGBN) ) + return AVPROBE_SCORE_MAX; + return 0; +} + +static const uint8_t deep_rgb24[] = {0, 0, 0, 3, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; +static const uint8_t deep_rgba[] = {0, 0, 0, 4, 0, 1, 0, 8, 0, 2, 0, 8, 0, 3, 0, 8}; +static const uint8_t deep_bgra[] = {0, 0, 0, 4, 0, 3, 0, 8, 0, 2, 0, 8, 0, 1, 0, 8}; +static const uint8_t deep_argb[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 1, 0, 8, 0, 2, 0, 8}; +static const uint8_t deep_abgr[] = {0, 0, 0, 4, 0,17, 0, 8, 0, 3, 0, 8, 0, 2, 0, 8}; + +static int iff_read_header(AVFormatContext *s) +{ + IffDemuxContext *iff = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + uint8_t *buf; + uint32_t chunk_id, data_size; + uint32_t screenmode = 0, num, den; + unsigned transparency = 0; + unsigned masking = 0; // no mask + uint8_t fmt[16]; + int fmt_size; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + avio_skip(pb, 8); + // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content + st->codec->codec_tag = avio_rl32(pb); + iff->bitmap_compression = -1; + iff->svx8_compression = -1; + iff->maud_bits = -1; + iff->maud_compression = -1; + + while(!url_feof(pb)) { + uint64_t orig_pos; + int res; + const char *metadata_tag = NULL; + chunk_id = avio_rl32(pb); + data_size = avio_rb32(pb); + orig_pos = avio_tell(pb); + + switch(chunk_id) { + case ID_VHDR: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + if (data_size < 14) + return AVERROR_INVALIDDATA; + avio_skip(pb, 12); + st->codec->sample_rate = avio_rb16(pb); + if (data_size >= 16) { + avio_skip(pb, 1); + iff->svx8_compression = avio_r8(pb); + } + break; + + case ID_MHDR: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + if (data_size < 32) + return AVERROR_INVALIDDATA; + avio_skip(pb, 4); + iff->maud_bits = avio_rb16(pb); + avio_skip(pb, 2); + num = avio_rb32(pb); + den = avio_rb16(pb); + if (!den) + return AVERROR_INVALIDDATA; + avio_skip(pb, 2); + st->codec->sample_rate = num / den; + st->codec->channels = avio_rb16(pb); + iff->maud_compression = avio_rb16(pb); + if (st->codec->channels == 1) + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + else if (st->codec->channels == 2) + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + break; + + case ID_ABIT: + case ID_BODY: + case ID_DBOD: + case ID_MDAT: + iff->body_pos = avio_tell(pb); + iff->body_end = iff->body_pos + data_size; + iff->body_size = data_size; + break; + + case ID_CHAN: + if (data_size < 4) + return AVERROR_INVALIDDATA; + if (avio_rb32(pb) < 6) { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } else { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } + break; + + case ID_CAMG: + if (data_size < 4) + return AVERROR_INVALIDDATA; + screenmode = avio_rb32(pb); + break; + + case ID_CMAP: + if (data_size < 3 || data_size > 768 || data_size % 3) { + av_log(s, AV_LOG_ERROR, "Invalid CMAP chunk size %d\n", + data_size); + return AVERROR_INVALIDDATA; + } + st->codec->extradata_size = data_size + IFF_EXTRA_VIDEO_SIZE; + st->codec->extradata = av_malloc(data_size + IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + if (avio_read(pb, st->codec->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0) + return AVERROR(EIO); + break; + + case ID_BMHD: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + if (data_size <= 8) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + avio_skip(pb, 4); // x, y offset + st->codec->bits_per_coded_sample = avio_r8(pb); + if (data_size >= 10) + masking = avio_r8(pb); + if (data_size >= 11) + iff->bitmap_compression = avio_r8(pb); + if (data_size >= 14) { + avio_skip(pb, 1); // padding + transparency = avio_rb16(pb); + } + if (data_size >= 16) { + st->sample_aspect_ratio.num = avio_r8(pb); + st->sample_aspect_ratio.den = avio_r8(pb); + } + break; + + case ID_DPEL: + if (data_size < 4 || (data_size & 3)) + return AVERROR_INVALIDDATA; + if ((fmt_size = avio_read(pb, fmt, sizeof(fmt))) < 0) + return fmt_size; + if (fmt_size == sizeof(deep_rgb24) && !memcmp(fmt, deep_rgb24, sizeof(deep_rgb24))) + st->codec->pix_fmt = AV_PIX_FMT_RGB24; + else if (fmt_size == sizeof(deep_rgba) && !memcmp(fmt, deep_rgba, sizeof(deep_rgba))) + st->codec->pix_fmt = AV_PIX_FMT_RGBA; + else if (fmt_size == sizeof(deep_bgra) && !memcmp(fmt, deep_bgra, sizeof(deep_bgra))) + st->codec->pix_fmt = AV_PIX_FMT_BGRA; + else if (fmt_size == sizeof(deep_argb) && !memcmp(fmt, deep_argb, sizeof(deep_argb))) + st->codec->pix_fmt = AV_PIX_FMT_ARGB; + else if (fmt_size == sizeof(deep_abgr) && !memcmp(fmt, deep_abgr, sizeof(deep_abgr))) + st->codec->pix_fmt = AV_PIX_FMT_ABGR; + else { + avpriv_request_sample(s, "color format %.16s", fmt); + return AVERROR_PATCHWELCOME; + } + break; + + case ID_DGBL: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + if (data_size < 8) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + iff->bitmap_compression = avio_rb16(pb); + st->sample_aspect_ratio.num = avio_r8(pb); + st->sample_aspect_ratio.den = avio_r8(pb); + st->codec->bits_per_coded_sample = 24; + break; + + case ID_DLOC: + if (data_size < 4) + return AVERROR_INVALIDDATA; + st->codec->width = avio_rb16(pb); + st->codec->height = avio_rb16(pb); + break; + + case ID_TVDC: + if (data_size < sizeof(iff->tvdc)) + return AVERROR_INVALIDDATA; + res = avio_read(pb, iff->tvdc, sizeof(iff->tvdc)); + if (res < 0) + return res; + break; + + case ID_ANNO: + case ID_TEXT: metadata_tag = "comment"; break; + case ID_AUTH: metadata_tag = "artist"; break; + case ID_COPYRIGHT: metadata_tag = "copyright"; break; + case ID_NAME: metadata_tag = "title"; break; + } + + if (metadata_tag) { + if ((res = get_metadata(s, metadata_tag, data_size)) < 0) { + av_log(s, AV_LOG_ERROR, "cannot allocate metadata tag %s!\n", metadata_tag); + return res; + } + } + avio_skip(pb, data_size - (avio_tell(pb) - orig_pos) + (data_size & 1)); + } + + avio_seek(pb, iff->body_pos, SEEK_SET); + + switch(st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); + + if (st->codec->codec_tag == ID_16SV) + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE_PLANAR; + else if (st->codec->codec_tag == ID_MAUD) { + if (iff->maud_bits == 8 && !iff->maud_compression) { + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + } else if (iff->maud_bits == 16 && !iff->maud_compression) { + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else if (iff->maud_bits == 8 && iff->maud_compression == 2) { + st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; + } else if (iff->maud_bits == 8 && iff->maud_compression == 3) { + st->codec->codec_id = AV_CODEC_ID_PCM_MULAW; + } else { + avpriv_request_sample(s, "compression %d and bit depth %d", iff->maud_compression, iff->maud_bits); + return AVERROR_PATCHWELCOME; + } + + st->codec->bits_per_coded_sample = + av_get_bits_per_sample(st->codec->codec_id); + + st->codec->block_align = + st->codec->bits_per_coded_sample * st->codec->channels / 8; + } else { + switch (iff->svx8_compression) { + case COMP_NONE: + st->codec->codec_id = AV_CODEC_ID_PCM_S8_PLANAR; + break; + case COMP_FIB: + st->codec->codec_id = AV_CODEC_ID_8SVX_FIB; + break; + case COMP_EXP: + st->codec->codec_id = AV_CODEC_ID_8SVX_EXP; + break; + default: + av_log(s, AV_LOG_ERROR, + "Unknown SVX8 compression method '%d'\n", iff->svx8_compression); + return -1; + } + } + + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * st->codec->bits_per_coded_sample; + st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; + break; + + case AVMEDIA_TYPE_VIDEO: + iff->bpp = st->codec->bits_per_coded_sample; + if ((screenmode & 0x800 /* Hold And Modify */) && iff->bpp <= 8) { + iff->ham = iff->bpp > 6 ? 6 : 4; + st->codec->bits_per_coded_sample = 24; + } + iff->flags = (screenmode & 0x80 /* Extra HalfBrite */) && iff->bpp <= 8; + iff->masking = masking; + iff->transparency = transparency; + + if (!st->codec->extradata) { + st->codec->extradata_size = IFF_EXTRA_VIDEO_SIZE; + st->codec->extradata = av_malloc(IFF_EXTRA_VIDEO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + } + av_assert0(st->codec->extradata_size >= IFF_EXTRA_VIDEO_SIZE); + buf = st->codec->extradata; + bytestream_put_be16(&buf, IFF_EXTRA_VIDEO_SIZE); + bytestream_put_byte(&buf, iff->bitmap_compression); + bytestream_put_byte(&buf, iff->bpp); + bytestream_put_byte(&buf, iff->ham); + bytestream_put_byte(&buf, iff->flags); + bytestream_put_be16(&buf, iff->transparency); + bytestream_put_byte(&buf, iff->masking); + bytestream_put_buffer(&buf, iff->tvdc, sizeof(iff->tvdc)); + st->codec->codec_id = AV_CODEC_ID_IFF_ILBM; + break; + default: + return -1; + } + + return 0; +} + +static int iff_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + IffDemuxContext *iff = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int ret; + int64_t pos = avio_tell(pb); + + if (pos >= iff->body_end) + return AVERROR_EOF; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st->codec->codec_tag == ID_MAUD) { + ret = av_get_packet(pb, pkt, FFMIN(iff->body_end - pos, 1024 * st->codec->block_align)); + } else { + ret = av_get_packet(pb, pkt, iff->body_size); + } + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + uint8_t *buf; + + if (av_new_packet(pkt, iff->body_size + 2) < 0) { + return AVERROR(ENOMEM); + } + + buf = pkt->data; + bytestream_put_be16(&buf, 2); + ret = avio_read(pb, buf, iff->body_size); + } else { + av_assert0(0); + } + + if (pos == iff->body_pos) + pkt->flags |= AV_PKT_FLAG_KEY; + if (ret < 0) + return ret; + pkt->stream_index = 0; + return ret; +} + +AVInputFormat ff_iff_demuxer = { + .name = "iff", + .long_name = NULL_IF_CONFIG_SMALL("IFF (Interchange File Format)"), + .priv_data_size = sizeof(IffDemuxContext), + .read_probe = iff_probe, + .read_header = iff_read_header, + .read_packet = iff_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iff.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/iff.o libavformat/iff.o: libavformat/iff.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/channel_layout.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/dict.h libavcodec/bytestream.h \ + libavutil/common.h libavutil/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iff.o Binary file ffmpeg/libavformat/iff.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ilbc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ilbc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,140 @@ +/* + * iLBC storage file format + * Copyright (c) 2012 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +static const char mode20_header[] = "#!iLBC20\n"; +static const char mode30_header[] = "#!iLBC30\n"; + +static int ilbc_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecContext *enc; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "Unsupported number of streams\n"); + return AVERROR(EINVAL); + } + enc = s->streams[0]->codec; + + if (enc->codec_id != AV_CODEC_ID_ILBC) { + av_log(s, AV_LOG_ERROR, "Unsupported codec\n"); + return AVERROR(EINVAL); + } + + if (enc->block_align == 50) { + avio_write(pb, mode30_header, sizeof(mode30_header) - 1); + } else if (enc->block_align == 38) { + avio_write(pb, mode20_header, sizeof(mode20_header) - 1); + } else { + av_log(s, AV_LOG_ERROR, "Unsupported mode\n"); + return AVERROR(EINVAL); + } + avio_flush(pb); + return 0; +} + +static int ilbc_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} + +static int ilbc_probe(AVProbeData *p) +{ + // Only check for "#!iLBC" which matches both formats + if (!memcmp(p->buf, mode20_header, 6)) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +static int ilbc_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + uint8_t header[9]; + + avio_read(pb, header, 9); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_id = AV_CODEC_ID_ILBC; + st->codec->sample_rate = 8000; + st->codec->channels = 1; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->start_time = 0; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + if (!memcmp(header, mode20_header, sizeof(mode20_header) - 1)) { + st->codec->block_align = 38; + st->codec->bit_rate = 15200; + } else if (!memcmp(header, mode30_header, sizeof(mode30_header) - 1)) { + st->codec->block_align = 50; + st->codec->bit_rate = 13333; + } else { + av_log(s, AV_LOG_ERROR, "Unrecognized iLBC file header\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static int ilbc_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + AVCodecContext *enc = s->streams[0]->codec; + int ret; + + if ((ret = av_new_packet(pkt, enc->block_align)) < 0) + return ret; + + pkt->stream_index = 0; + pkt->pos = avio_tell(s->pb); + pkt->duration = enc->block_align == 38 ? 160 : 240; + if ((ret = avio_read(s->pb, pkt->data, enc->block_align)) != enc->block_align) { + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR(EIO); + } + + return 0; +} + +AVInputFormat ff_ilbc_demuxer = { + .name = "ilbc", + .long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), + .read_probe = ilbc_probe, + .read_header = ilbc_read_header, + .read_packet = ilbc_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; + +AVOutputFormat ff_ilbc_muxer = { + .name = "ilbc", + .long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), + .mime_type = "audio/iLBC", + .extensions = "lbc", + .audio_codec = AV_CODEC_ID_ILBC, + .write_header = ilbc_write_header, + .write_packet = ilbc_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ilbc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ilbc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/ilbc.o libavformat/ilbc.o: libavformat/ilbc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ilbc.o Binary file ffmpeg/libavformat/ilbc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,102 @@ +/* + * Image format + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * Copyright (c) 2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "internal.h" + +typedef struct { + enum AVCodecID id; + const char *str; +} IdStrMap; + +static const IdStrMap img_tags[] = { + { AV_CODEC_ID_MJPEG, "jpeg" }, + { AV_CODEC_ID_MJPEG, "jpg" }, + { AV_CODEC_ID_MJPEG, "jps" }, + { AV_CODEC_ID_LJPEG, "ljpg" }, + { AV_CODEC_ID_JPEGLS, "jls" }, + { AV_CODEC_ID_PNG, "png" }, + { AV_CODEC_ID_PNG, "pns" }, + { AV_CODEC_ID_PNG, "mng" }, + { AV_CODEC_ID_PPM, "ppm" }, + { AV_CODEC_ID_PPM, "pnm" }, + { AV_CODEC_ID_PGM, "pgm" }, + { AV_CODEC_ID_PGMYUV, "pgmyuv" }, + { AV_CODEC_ID_PBM, "pbm" }, + { AV_CODEC_ID_PAM, "pam" }, + { AV_CODEC_ID_MPEG1VIDEO, "mpg1-img" }, + { AV_CODEC_ID_MPEG2VIDEO, "mpg2-img" }, + { AV_CODEC_ID_MPEG4, "mpg4-img" }, + { AV_CODEC_ID_FFV1, "ffv1-img" }, + { AV_CODEC_ID_RAWVIDEO, "y" }, + { AV_CODEC_ID_RAWVIDEO, "raw" }, + { AV_CODEC_ID_BMP, "bmp" }, + { AV_CODEC_ID_TARGA, "tga" }, + { AV_CODEC_ID_TIFF, "tiff" }, + { AV_CODEC_ID_TIFF, "tif" }, + { AV_CODEC_ID_SGI, "sgi" }, + { AV_CODEC_ID_PTX, "ptx" }, + { AV_CODEC_ID_PCX, "pcx" }, + { AV_CODEC_ID_BRENDER_PIX, "pix" }, + { AV_CODEC_ID_SUNRAST, "sun" }, + { AV_CODEC_ID_SUNRAST, "ras" }, + { AV_CODEC_ID_SUNRAST, "rs" }, + { AV_CODEC_ID_SUNRAST, "im1" }, + { AV_CODEC_ID_SUNRAST, "im8" }, + { AV_CODEC_ID_SUNRAST, "im24" }, + { AV_CODEC_ID_SUNRAST, "im32" }, + { AV_CODEC_ID_SUNRAST, "sunras" }, + { AV_CODEC_ID_JPEG2000, "j2c" }, + { AV_CODEC_ID_JPEG2000, "jp2" }, + { AV_CODEC_ID_JPEG2000, "jpc" }, + { AV_CODEC_ID_JPEG2000, "j2k" }, + { AV_CODEC_ID_DPX, "dpx" }, + { AV_CODEC_ID_EXR, "exr" }, + { AV_CODEC_ID_PICTOR, "pic" }, + { AV_CODEC_ID_V210X, "yuv10" }, + { AV_CODEC_ID_WEBP, "webp" }, + { AV_CODEC_ID_XBM, "xbm" }, + { AV_CODEC_ID_XFACE, "xface" }, + { AV_CODEC_ID_XWD, "xwd" }, + { AV_CODEC_ID_NONE, NULL } +}; + +static enum AVCodecID av_str2id(const IdStrMap *tags, const char *str) +{ + str = strrchr(str, '.'); + if (!str) + return AV_CODEC_ID_NONE; + str++; + + while (tags->id) { + if (!av_strcasecmp(str, tags->str)) + return tags->id; + + tags++; + } + return AV_CODEC_ID_NONE; +} + +enum AVCodecID ff_guess_image2_codec(const char *filename) +{ + return av_str2id(img_tags, filename); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/img2.o libavformat/img2.o: libavformat/img2.c \ + libavutil/avstring.h libavutil/attributes.h libavformat/internal.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2.o Binary file ffmpeg/libavformat/img2.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,485 @@ +/* + * Image format + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * Copyright (c) 2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/parseutils.h" +#include "avformat.h" +#include "internal.h" +#if HAVE_GLOB +#include + +/* Locally define as 0 (bitwise-OR no-op) any missing glob options that + are non-posix glibc/bsd extensions. */ +#ifndef GLOB_NOMAGIC +#define GLOB_NOMAGIC 0 +#endif +#ifndef GLOB_BRACE +#define GLOB_BRACE 0 +#endif + +#endif /* HAVE_GLOB */ + +typedef struct { + const AVClass *class; /**< Class for private options. */ + int img_first; + int img_last; + int img_number; + int64_t pts; + int img_count; + int is_pipe; + int split_planes; /**< use independent file for each Y, U, V plane */ + char path[1024]; + char *pixel_format; /**< Set by a private option. */ + int width, height; /**< Set by a private option. */ + AVRational framerate; /**< Set by a private option. */ + int loop; + enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type; + int use_glob; +#if HAVE_GLOB + glob_t globstate; +#endif + int start_number; + int start_number_range; + int frame_size; +} VideoDemuxData; + +static const int sizes[][2] = { + { 640, 480 }, + { 720, 480 }, + { 720, 576 }, + { 352, 288 }, + { 352, 240 }, + { 160, 128 }, + { 512, 384 }, + { 640, 352 }, + { 640, 240 }, +}; + +static int infer_size(int *width_ptr, int *height_ptr, int size) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(sizes); i++) { + if ((sizes[i][0] * sizes[i][1]) == size) { + *width_ptr = sizes[i][0]; + *height_ptr = sizes[i][1]; + return 0; + } + } + + return -1; +} + +static int is_glob(const char *path) +{ +#if HAVE_GLOB + size_t span = 0; + const char *p = path; + + while (p = strchr(p, '%')) { + if (*(++p) == '%') { + ++p; + continue; + } + if (span = strspn(p, "*?[]{}")) + break; + } + /* Did we hit a glob char or get to the end? */ + return span != 0; +#else + return 0; +#endif +} + +/** + * Get index range of image files matched by path. + * + * @param pfirst_index pointer to index updated with the first number in the range + * @param plast_index pointer to index updated with the last number in the range + * @param path path which has to be matched by the image files in the range + * @param start_index minimum accepted value for the first index in the range + * @return -1 if no image file could be found + */ +static int find_image_range(int *pfirst_index, int *plast_index, + const char *path, int start_index, int start_index_range) +{ + char buf[1024]; + int range, last_index, range1, first_index; + + /* find the first image */ + for (first_index = start_index; first_index < start_index + start_index_range; first_index++) { + if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0) { + *pfirst_index = + *plast_index = 1; + if (avio_check(buf, AVIO_FLAG_READ) > 0) + return 0; + return -1; + } + if (avio_check(buf, AVIO_FLAG_READ) > 0) + break; + } + if (first_index == start_index + start_index_range) + goto fail; + + /* find the last image */ + last_index = first_index; + for (;;) { + range = 0; + for (;;) { + if (!range) + range1 = 1; + else + range1 = 2 * range; + if (av_get_frame_filename(buf, sizeof(buf), path, + last_index + range1) < 0) + goto fail; + if (avio_check(buf, AVIO_FLAG_READ) <= 0) + break; + range = range1; + /* just in case... */ + if (range >= (1 << 30)) + goto fail; + } + /* we are sure than image last_index + range exists */ + if (!range) + break; + last_index += range; + } + *pfirst_index = first_index; + *plast_index = last_index; + return 0; + +fail: + return -1; +} + +static int img_read_probe(AVProbeData *p) +{ + if (p->filename && ff_guess_image2_codec(p->filename)) { + if (av_filename_number_test(p->filename)) + return AVPROBE_SCORE_MAX; + else if (is_glob(p->filename)) + return AVPROBE_SCORE_MAX; + else if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif")) + return 5; + else + return AVPROBE_SCORE_MAX / 2; + } + return 0; +} + +static int img_read_header(AVFormatContext *s1) +{ + VideoDemuxData *s = s1->priv_data; + int first_index, last_index; + AVStream *st; + enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; + + s1->ctx_flags |= AVFMTCTX_NOHEADER; + + st = avformat_new_stream(s1, NULL); + if (!st) { + return AVERROR(ENOMEM); + } + + if (s->pixel_format && + (pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { + av_log(s1, AV_LOG_ERROR, "No such pixel format: %s.\n", + s->pixel_format); + return AVERROR(EINVAL); + } + + av_strlcpy(s->path, s1->filename, sizeof(s->path)); + s->img_number = 0; + s->img_count = 0; + + /* find format */ + if (s1->iformat->flags & AVFMT_NOFILE) + s->is_pipe = 0; + else { + s->is_pipe = 1; + st->need_parsing = AVSTREAM_PARSE_FULL; + } + + avpriv_set_pts_info(st, 60, s->framerate.den, s->framerate.num); + + if (s->width && s->height) { + st->codec->width = s->width; + st->codec->height = s->height; + } + + if (!s->is_pipe) { + if (s->pattern_type == PT_GLOB_SEQUENCE) { + s->use_glob = is_glob(s->path); + if (s->use_glob) { + char *p = s->path, *q, *dup; + int gerr; + + av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: " + "use pattern_type 'glob' instead\n"); +#if HAVE_GLOB + dup = q = av_strdup(p); + while (*q) { + /* Do we have room for the next char and a \ insertion? */ + if ((p - s->path) >= (sizeof(s->path) - 2)) + break; + if (*q == '%' && strspn(q + 1, "%*?[]{}")) + ++q; + else if (strspn(q, "\\*?[]{}")) + *p++ = '\\'; + *p++ = *q++; + } + *p = 0; + av_free(dup); + + gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); + if (gerr != 0) { + return AVERROR(ENOENT); + } + first_index = 0; + last_index = s->globstate.gl_pathc - 1; +#endif + } + } + if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) { + if (find_image_range(&first_index, &last_index, s->path, + s->start_number, s->start_number_range) < 0) { + av_log(s1, AV_LOG_ERROR, + "Could find no file with with path '%s' and index in the range %d-%d\n", + s->path, s->start_number, s->start_number + s->start_number_range - 1); + return AVERROR(ENOENT); + } + } else if (s->pattern_type == PT_GLOB) { +#if HAVE_GLOB + int gerr; + gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); + if (gerr != 0) { + return AVERROR(ENOENT); + } + first_index = 0; + last_index = s->globstate.gl_pathc - 1; + s->use_glob = 1; +#else + av_log(s1, AV_LOG_ERROR, + "Pattern type 'glob' was selected but globbing " + "is not supported by this libavformat build\n"); + return AVERROR(ENOSYS); +#endif + } else if (s->pattern_type != PT_GLOB_SEQUENCE) { + av_log(s1, AV_LOG_ERROR, + "Unknown value '%d' for pattern_type option\n", s->pattern_type); + return AVERROR(EINVAL); + } + s->img_first = first_index; + s->img_last = last_index; + s->img_number = first_index; + /* compute duration */ + st->start_time = 0; + st->duration = last_index - first_index + 1; + } + + if (s1->video_codec_id) { + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = s1->video_codec_id; + } else if (s1->audio_codec_id) { + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s1->audio_codec_id; + } else { + const char *str = strrchr(s->path, '.'); + s->split_planes = str && !av_strcasecmp(str + 1, "y"); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = ff_guess_image2_codec(s->path); + if (st->codec->codec_id == AV_CODEC_ID_LJPEG) + st->codec->codec_id = AV_CODEC_ID_MJPEG; + } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + pix_fmt != AV_PIX_FMT_NONE) + st->codec->pix_fmt = pix_fmt; + + return 0; +} + +static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) +{ + VideoDemuxData *s = s1->priv_data; + char filename_bytes[1024]; + char *filename = filename_bytes; + int i; + int size[3] = { 0 }, ret[3] = { 0 }; + AVIOContext *f[3] = { NULL }; + AVCodecContext *codec = s1->streams[0]->codec; + + if (!s->is_pipe) { + /* loop over input */ + if (s->loop && s->img_number > s->img_last) { + s->img_number = s->img_first; + } + if (s->img_number > s->img_last) + return AVERROR_EOF; + if (s->use_glob) { +#if HAVE_GLOB + filename = s->globstate.gl_pathv[s->img_number]; +#endif + } else { + if (av_get_frame_filename(filename_bytes, sizeof(filename_bytes), + s->path, + s->img_number) < 0 && s->img_number > 1) + return AVERROR(EIO); + } + for (i = 0; i < 3; i++) { + if (avio_open2(&f[i], filename, AVIO_FLAG_READ, + &s1->interrupt_callback, NULL) < 0) { + if (i >= 1) + break; + av_log(s1, AV_LOG_ERROR, "Could not open file : %s\n", + filename); + return AVERROR(EIO); + } + size[i] = avio_size(f[i]); + + if (!s->split_planes) + break; + filename[strlen(filename) - 1] = 'U' + i; + } + + if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->width) + infer_size(&codec->width, &codec->height, size[0]); + } else { + f[0] = s1->pb; + if (url_feof(f[0])) + return AVERROR(EIO); + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else { + size[0] = 4096; + } + } + + if (av_new_packet(pkt, size[0] + size[1] + size[2]) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 0; + pkt->flags |= AV_PKT_FLAG_KEY; + if (!s->is_pipe) + pkt->pts = s->pts; + + pkt->size = 0; + for (i = 0; i < 3; i++) { + if (f[i]) { + ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]); + if (!s->is_pipe) + avio_close(f[i]); + if (ret[i] > 0) + pkt->size += ret[i]; + } + } + + if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) { + av_free_packet(pkt); + return AVERROR(EIO); /* signal EOF */ + } else { + s->img_count++; + s->img_number++; + s->pts++; + return 0; + } +} + +static int img_read_close(struct AVFormatContext* s1) +{ + VideoDemuxData *s = s1->priv_data; +#if HAVE_GLOB + if (s->use_glob) { + globfree(&s->globstate); + } +#endif + return 0; +} + +static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + VideoDemuxData *s1 = s->priv_data; + + if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first) + return -1; + s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first; + s1->pts = timestamp; + return 0; +} + +#define OFFSET(x) offsetof(VideoDemuxData, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "framerate", "set the video framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, + { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, + + { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"}, + { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, + + { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, + { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, + { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.i64 = 5}, 1, INT_MAX, DEC }, + { "video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, + { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, + { NULL }, +}; + +#if CONFIG_IMAGE2_DEMUXER +static const AVClass img2_class = { + .class_name = "image2 demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; +AVInputFormat ff_image2_demuxer = { + .name = "image2", + .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), + .priv_data_size = sizeof(VideoDemuxData), + .read_probe = img_read_probe, + .read_header = img_read_header, + .read_packet = img_read_packet, + .read_close = img_read_close, + .read_seek = img_read_seek, + .flags = AVFMT_NOFILE, + .priv_class = &img2_class, +}; +#endif +#if CONFIG_IMAGE2PIPE_DEMUXER +static const AVClass img2pipe_class = { + .class_name = "image2pipe demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; +AVInputFormat ff_image2pipe_demuxer = { + .name = "image2pipe", + .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), + .priv_data_size = sizeof(VideoDemuxData), + .read_header = img_read_header, + .read_packet = img_read_packet, + .priv_class = &img2pipe_class, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/img2dec.o libavformat/img2dec.o: libavformat/img2dec.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/log.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/opt.h libavutil/samplefmt.h libavutil/pixdesc.h \ + libavutil/parseutils.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2dec.o Binary file ffmpeg/libavformat/img2dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2enc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2enc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,171 @@ +/* + * Image format + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * Copyright (c) 2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/avstring.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; /**< Class for private options. */ + int img_number; + int is_pipe; + int split_planes; /**< use independent file for each Y, U, V plane */ + char path[1024]; + int update; +} VideoMuxData; + +static int write_header(AVFormatContext *s) +{ + VideoMuxData *img = s->priv_data; + AVStream *st = s->streams[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codec->pix_fmt); + const char *str; + + av_strlcpy(img->path, s->filename, sizeof(img->path)); + + /* find format */ + if (s->oformat->flags & AVFMT_NOFILE) + img->is_pipe = 0; + else + img->is_pipe = 1; + + str = strrchr(img->path, '.'); + img->split_planes = str + && !av_strcasecmp(str + 1, "y") + && s->nb_streams == 1 + && st->codec->codec_id == AV_CODEC_ID_RAWVIDEO + && desc + &&(desc->flags & PIX_FMT_PLANAR) + && desc->nb_components >= 3; + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + VideoMuxData *img = s->priv_data; + AVIOContext *pb[4]; + char filename[1024]; + AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(codec->pix_fmt); + int i; + + if (!img->is_pipe) { + if (img->update) { + av_strlcpy(filename, img->path, sizeof(filename)); + } else if (av_get_frame_filename(filename, sizeof(filename), img->path, img->img_number) < 0 && + img->img_number > 1) { + av_log(s, AV_LOG_ERROR, + "Could not get frame filename number %d from pattern '%s' (either set updatefirst or use a pattern like %%03d within the filename pattern)\n", + img->img_number, img->path); + return AVERROR(EINVAL); + } + for (i = 0; i < 4; i++) { + if (avio_open2(&pb[i], filename, AVIO_FLAG_WRITE, + &s->interrupt_callback, NULL) < 0) { + av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", filename); + return AVERROR(EIO); + } + + if (!img->split_planes || i+1 >= desc->nb_components) + break; + filename[strlen(filename) - 1] = ((int[]){'U','V','A','x'})[i]; + } + } else { + pb[0] = s->pb; + } + + if (img->split_planes) { + int ysize = codec->width * codec->height; + int usize = ((-codec->width)>>desc->log2_chroma_w) * ((-codec->height)>>desc->log2_chroma_h); + if (desc->comp[0].depth_minus1 >= 8) { + ysize *= 2; + usize *= 2; + } + avio_write(pb[0], pkt->data , ysize); + avio_write(pb[1], pkt->data + ysize , usize); + avio_write(pb[2], pkt->data + ysize + usize, usize); + avio_close(pb[1]); + avio_close(pb[2]); + if (desc->nb_components > 3) { + avio_write(pb[3], pkt->data + ysize + 2*usize, ysize); + avio_close(pb[3]); + } + } else { + avio_write(pb[0], pkt->data, pkt->size); + } + avio_flush(pb[0]); + if (!img->is_pipe) { + avio_close(pb[0]); + } + + img->img_number++; + return 0; +} + +#define OFFSET(x) offsetof(VideoMuxData, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption muxoptions[] = { + { "updatefirst", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, + { "update", "continuously overwrite one file", OFFSET(update), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, ENC }, + { "start_number", "set first number in the sequence", OFFSET(img_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, ENC }, + { NULL }, +}; + +#if CONFIG_IMAGE2_MUXER +static const AVClass img2mux_class = { + .class_name = "image2 muxer", + .item_name = av_default_item_name, + .option = muxoptions, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_image2_muxer = { + .name = "image2", + .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), + .extensions = "bmp,dpx,jls,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png," + "ppm,sgi,tga,tif,tiff,jp2,j2c,xwd,sun,ras,rs,im1,im8,im24," + "sunras,xbm,xface", + .priv_data_size = sizeof(VideoMuxData), + .video_codec = AV_CODEC_ID_MJPEG, + .write_header = write_header, + .write_packet = write_packet, + .flags = AVFMT_NOTIMESTAMPS | AVFMT_NODIMENSIONS | AVFMT_NOFILE, + .priv_class = &img2mux_class, +}; +#endif +#if CONFIG_IMAGE2PIPE_MUXER +AVOutputFormat ff_image2pipe_muxer = { + .name = "image2pipe", + .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), + .priv_data_size = sizeof(VideoMuxData), + .video_codec = AV_CODEC_ID_MJPEG, + .write_header = write_header, + .write_packet = write_packet, + .flags = AVFMT_NOTIMESTAMPS | AVFMT_NODIMENSIONS +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2enc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/img2enc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/img2enc.o libavformat/img2enc.o: libavformat/img2enc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/avstring.h libavutil/log.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/opt.h \ + libavutil/samplefmt.h libavutil/pixdesc.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/img2enc.o Binary file ffmpeg/libavformat/img2enc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ingenientdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ingenientdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,66 @@ +/* + * RAW Ingenient MJPEG demuxer + * Copyright (c) 2005 Alex Beregszaszi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +// http://www.artificis.hu/files/texts/ingenient.txt +static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size, w, h, unk1, unk2; + + if (avio_rl32(s->pb) != MKTAG('M', 'J', 'P', 'G')) + return AVERROR(EIO); // FIXME + + size = avio_rl32(s->pb); + + w = avio_rl16(s->pb); + h = avio_rl16(s->pb); + + avio_skip(s->pb, 8); // zero + size (padded?) + avio_skip(s->pb, 2); + unk1 = avio_rl16(s->pb); + unk2 = avio_rl16(s->pb); + avio_skip(s->pb, 22); // ASCII timestamp + + av_log(s, AV_LOG_DEBUG, "Ingenient packet: size=%d, width=%d, height=%d, unk1=%d unk2=%d\n", + size, w, h, unk1, unk2); + + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + pkt->stream_index = 0; + return ret; +} + +FF_RAWVIDEO_DEMUXER_CLASS(ingenient) + +AVInputFormat ff_ingenient_demuxer = { + .name = "ingenient", + .long_name = NULL_IF_CONFIG_SMALL("raw Ingenient MJPEG"), + .priv_data_size = sizeof(FFRawVideoDemuxerContext), + .read_header = ff_raw_video_read_header, + .read_packet = ingenient_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "cgi", // FIXME + .raw_codec_id = AV_CODEC_ID_MJPEG, + .priv_class = &ingenient_demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ingenientdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ingenientdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/ingenientdec.o libavformat/ingenientdec.o: \ + libavformat/ingenientdec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ingenientdec.o Binary file ffmpeg/libavformat/ingenientdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/internal.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,395 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_INTERNAL_H +#define AVFORMAT_INTERNAL_H + +#include +#include "avformat.h" + +#define MAX_URL_SIZE 4096 + +#ifdef DEBUG +# define hex_dump_debug(class, buf, size) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size) +#else +# define hex_dump_debug(class, buf, size) +#endif + +typedef struct AVCodecTag { + enum AVCodecID id; + unsigned int tag; +} AVCodecTag; + +typedef struct CodecMime{ + char str[32]; + enum AVCodecID id; +} CodecMime; + +#ifdef __GNUC__ +#define dynarray_add(tab, nb_ptr, elem)\ +do {\ + __typeof__(tab) _tab = (tab);\ + __typeof__(elem) _elem = (elem);\ + (void)sizeof(**_tab == _elem); /* check that types are compatible */\ + av_dynarray_add(_tab, nb_ptr, _elem);\ +} while(0) +#else +#define dynarray_add(tab, nb_ptr, elem)\ +do {\ + av_dynarray_add((tab), nb_ptr, (elem));\ +} while(0) +#endif + +struct tm *ff_brktimegm(time_t secs, struct tm *tm); + +char *ff_data_to_hex(char *buf, const uint8_t *src, int size, int lowercase); + +/** + * Parse a string of hexadecimal strings. Any space between the hexadecimal + * digits is ignored. + * + * @param data if non-null, the parsed data is written to this pointer + * @param p the string to parse + * @return the number of bytes written (or to be written, if data is null) + */ +int ff_hex_to_data(uint8_t *data, const char *p); + +void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx); + +/** + * Add packet to AVFormatContext->packet_buffer list, determining its + * interleaved position using compare() function argument. + * @return 0, or < 0 on error + */ +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, + int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)); + +void ff_read_frame_flush(AVFormatContext *s); + +#define NTP_OFFSET 2208988800ULL +#define NTP_OFFSET_US (NTP_OFFSET * 1000000ULL) + +/** Get the current time since NTP epoch in microseconds. */ +uint64_t ff_ntp_time(void); + +/** + * Assemble a URL string from components. This is the reverse operation + * of av_url_split. + * + * Note, this requires networking to be initialized, so the caller must + * ensure ff_network_init has been called. + * + * @see av_url_split + * + * @param str the buffer to fill with the url + * @param size the size of the str buffer + * @param proto the protocol identifier, if null, the separator + * after the identifier is left out, too + * @param authorization an optional authorization string, may be null. + * An empty string is treated the same as a null string. + * @param hostname the host name string + * @param port the port number, left out from the string if negative + * @param fmt a generic format string for everything to add after the + * host/port, may be null + * @return the number of characters written to the destination buffer + */ +int ff_url_join(char *str, int size, const char *proto, + const char *authorization, const char *hostname, + int port, const char *fmt, ...) av_printf_format(7, 8); + +/** + * Append the media-specific SDP fragment for the media stream c + * to the buffer buff. + * + * Note, the buffer needs to be initialized, since it is appended to + * existing content. + * + * @param buff the buffer to append the SDP fragment to + * @param size the size of the buff buffer + * @param st the AVStream of the media to describe + * @param idx the global stream index + * @param dest_addr the destination address of the media stream, may be NULL + * @param dest_type the destination address type, may be NULL + * @param port the destination port of the media stream, 0 if unknown + * @param ttl the time to live of the stream, 0 if not multicast + * @param fmt the AVFormatContext, which might contain options modifying + * the generated SDP + */ +void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx, + const char *dest_addr, const char *dest_type, + int port, int ttl, AVFormatContext *fmt); + +/** + * Write a packet to another muxer than the one the user originally + * intended. Useful when chaining muxers, where one muxer internally + * writes a received packet to another muxer. + * + * @param dst the muxer to write the packet to + * @param dst_stream the stream index within dst to write the packet to + * @param pkt the packet to be written + * @param src the muxer the packet originally was intended for + * @return the value av_write_frame returned + */ +int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, + AVFormatContext *src); + +/** + * Get the length in bytes which is needed to store val as v. + */ +int ff_get_v_length(uint64_t val); + +/** + * Put val using a variable number of bytes. + */ +void ff_put_v(AVIOContext *bc, uint64_t val); + +/** + * Read a whole line of text from AVIOContext. Stop reading after reaching + * either a \\n, a \\0 or EOF. The returned string is always \\0-terminated, + * and may be truncated if the buffer is too small. + * + * @param s the read-only AVIOContext + * @param buf buffer to store the read line + * @param maxlen size of the buffer + * @return the length of the string written in the buffer, not including the + * final \\0 + */ +int ff_get_line(AVIOContext *s, char *buf, int maxlen); + +#define SPACE_CHARS " \t\r\n" + +/** + * Callback function type for ff_parse_key_value. + * + * @param key a pointer to the key + * @param key_len the number of bytes that belong to the key, including the '=' + * char + * @param dest return the destination pointer for the value in *dest, may + * be null to ignore the value + * @param dest_len the length of the *dest buffer + */ +typedef void (*ff_parse_key_val_cb)(void *context, const char *key, + int key_len, char **dest, int *dest_len); +/** + * Parse a string with comma-separated key=value pairs. The value strings + * may be quoted and may contain escaped characters within quoted strings. + * + * @param str the string to parse + * @param callback_get_buf function that returns where to store the + * unescaped value string. + * @param context the opaque context pointer to pass to callback_get_buf + */ +void ff_parse_key_value(const char *str, ff_parse_key_val_cb callback_get_buf, + void *context); + +/** + * Find stream index based on format-specific stream ID + * @return stream index, or < 0 on error + */ +int ff_find_stream_index(AVFormatContext *s, int id); + +/** + * Internal version of av_index_search_timestamp + */ +int ff_index_search_timestamp(const AVIndexEntry *entries, int nb_entries, + int64_t wanted_timestamp, int flags); + +/** + * Internal version of av_add_index_entry + */ +int ff_add_index_entry(AVIndexEntry **index_entries, + int *nb_index_entries, + unsigned int *index_entries_allocated_size, + int64_t pos, int64_t timestamp, int size, int distance, int flags); + +/** + * Add a new chapter. + * + * @param s media file handle + * @param id unique ID for this chapter + * @param start chapter start time in time_base units + * @param end chapter end time in time_base units + * @param title chapter title + * + * @return AVChapter or NULL on error + */ +AVChapter *avpriv_new_chapter(AVFormatContext *s, int id, AVRational time_base, + int64_t start, int64_t end, const char *title); + +/** + * Ensure the index uses less memory than the maximum specified in + * AVFormatContext.max_index_size by discarding entries if it grows + * too large. + */ +void ff_reduce_index(AVFormatContext *s, int stream_index); + +/** + * Convert a relative url into an absolute url, given a base url. + * + * @param buf the buffer where output absolute url is written + * @param size the size of buf + * @param base the base url, may be equal to buf. + * @param rel the new url, which is interpreted relative to base + */ +void ff_make_absolute_url(char *buf, int size, const char *base, + const char *rel); + +enum AVCodecID ff_guess_image2_codec(const char *filename); + +/** + * Convert a date string in ISO8601 format to Unix timestamp. + */ +int64_t ff_iso8601_to_unix_time(const char *datestr); + +/** + * Perform a binary search using av_index_search_timestamp() and + * AVInputFormat.read_timestamp(). + * + * @param target_ts target timestamp in the time base of the given stream + * @param stream_index stream number + */ +int ff_seek_frame_binary(AVFormatContext *s, int stream_index, + int64_t target_ts, int flags); + +/** + * Update cur_dts of all streams based on the given timestamp and AVStream. + * + * Stream ref_st unchanged, others set cur_dts in their native time base. + * Only needed for timestamp wrapping or if (dts not set and pts!=dts). + * @param timestamp new dts expressed in time_base of param ref_st + * @param ref_st reference stream giving time_base of param timestamp + */ +void ff_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp); + +/** + * Perform a binary search using read_timestamp(). + * + * @param target_ts target timestamp in the time base of the given stream + * @param stream_index stream number + */ +int64_t ff_gen_search(AVFormatContext *s, int stream_index, + int64_t target_ts, int64_t pos_min, + int64_t pos_max, int64_t pos_limit, + int64_t ts_min, int64_t ts_max, + int flags, int64_t *ts_ret, + int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )); + +/** + * Set the time base and wrapping info for a given stream. This will be used + * to interpret the stream's timestamps. If the new time base is invalid + * (numerator or denominator are non-positive), it leaves the stream + * unchanged. + * + * @param s stream + * @param pts_wrap_bits number of bits effectively used by the pts + * (used for wrap control) + * @param pts_num time base numerator + * @param pts_den time base denominator + */ +void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, + unsigned int pts_num, unsigned int pts_den); + +/** + * Add side data to a packet for changing parameters to the given values. + * Parameters set to 0 aren't included in the change. + */ +int ff_add_param_change(AVPacket *pkt, int32_t channels, + uint64_t channel_layout, int32_t sample_rate, + int32_t width, int32_t height); + +/** + * Set the timebase for each stream from the corresponding codec timebase and + * print it. + */ +int ff_framehash_write_header(AVFormatContext *s); + +/** + * Read a transport packet from a media file. + * + * @param s media file handle + * @param pkt is filled + * @return 0 if OK, AVERROR_xxx on error + */ +int ff_read_packet(AVFormatContext *s, AVPacket *pkt); + +/** + * Interleave a packet per dts in an output media file. + * + * Packets with pkt->destruct == av_destruct_packet will be freed inside this + * function, so they cannot be used after it. Note that calling av_free_packet() + * on them is still safe. + * + * @param s media file handle + * @param out the interleaved packet will be output here + * @param pkt the input packet + * @param flush 1 if no further packets are available as input and all + * remaining packets should be output + * @return 1 if a packet was output, 0 if no packet could be output, + * < 0 if an error occurred + */ +int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, + AVPacket *pkt, int flush); + +void ff_free_stream(AVFormatContext *s, AVStream *st); + +/** + * Return the frame duration in seconds. Return 0 if not available. + */ +void ff_compute_frame_duration(int *pnum, int *pden, AVStream *st, + AVCodecParserContext *pc, AVPacket *pkt); + +int ff_get_audio_frame_size(AVCodecContext *enc, int size, int mux); + +unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id); + +enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); + +/** + * Select a PCM codec based on the given parameters. + * + * @param bps bits-per-sample + * @param flt floating-point + * @param be big-endian + * @param sflags signed flags. each bit corresponds to one byte of bit depth. + * e.g. the 1st bit indicates if 8-bit should be signed or + * unsigned, the 2nd bit indicates if 16-bit should be signed or + * unsigned, etc... This is useful for formats such as WAVE where + * only 8-bit is unsigned and all other bit depths are signed. + * @return a PCM codec id or AV_CODEC_ID_NONE + */ +enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags); + +/** + * Chooses a timebase for muxing the specified stream. + * + * The choosen timebase allows sample accurate timestamps based + * on the framerate or sample rate for audio streams. It also is + * at least as precisse as 1/min_precission would be. + */ +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission); + +/** + * Generate standard extradata for AVC-Intra based on width/height and field order. + */ +void ff_generate_avci_extradata(AVStream *st); + +int ff_http_match_no_proxy(const char *no_proxy, const char *hostname); + +#endif /* AVFORMAT_INTERNAL_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ipmovie.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ipmovie.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,656 @@ +/* + * Interplay MVE File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Interplay MVE file demuxer + * by Mike Melanson (melanson@pcisys.net) + * For more information regarding the Interplay MVE file format, visit: + * http://www.pcisys.net/~melanson/codecs/ + * The aforementioned site also contains a command line utility for parsing + * IP MVE files so that you can get a good idea of the typical structure of + * such files. This demuxer is not the best example to use if you are trying + * to write your own as it uses a rather roundabout approach for splitting + * up and sending out the chunks. + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define CHUNK_PREAMBLE_SIZE 4 +#define OPCODE_PREAMBLE_SIZE 4 + +#define CHUNK_INIT_AUDIO 0x0000 +#define CHUNK_AUDIO_ONLY 0x0001 +#define CHUNK_INIT_VIDEO 0x0002 +#define CHUNK_VIDEO 0x0003 +#define CHUNK_SHUTDOWN 0x0004 +#define CHUNK_END 0x0005 +/* these last types are used internally */ +#define CHUNK_DONE 0xFFFC +#define CHUNK_NOMEM 0xFFFD +#define CHUNK_EOF 0xFFFE +#define CHUNK_BAD 0xFFFF + +#define OPCODE_END_OF_STREAM 0x00 +#define OPCODE_END_OF_CHUNK 0x01 +#define OPCODE_CREATE_TIMER 0x02 +#define OPCODE_INIT_AUDIO_BUFFERS 0x03 +#define OPCODE_START_STOP_AUDIO 0x04 +#define OPCODE_INIT_VIDEO_BUFFERS 0x05 +#define OPCODE_UNKNOWN_06 0x06 +#define OPCODE_SEND_BUFFER 0x07 +#define OPCODE_AUDIO_FRAME 0x08 +#define OPCODE_SILENCE_FRAME 0x09 +#define OPCODE_INIT_VIDEO_MODE 0x0A +#define OPCODE_CREATE_GRADIENT 0x0B +#define OPCODE_SET_PALETTE 0x0C +#define OPCODE_SET_PALETTE_COMPRESSED 0x0D +#define OPCODE_UNKNOWN_0E 0x0E +#define OPCODE_SET_DECODING_MAP 0x0F +#define OPCODE_UNKNOWN_10 0x10 +#define OPCODE_VIDEO_DATA 0x11 +#define OPCODE_UNKNOWN_12 0x12 +#define OPCODE_UNKNOWN_13 0x13 +#define OPCODE_UNKNOWN_14 0x14 +#define OPCODE_UNKNOWN_15 0x15 + +#define PALETTE_COUNT 256 + +typedef struct IPMVEContext { + + unsigned char *buf; + int buf_size; + + uint64_t frame_pts_inc; + + unsigned int video_bpp; + unsigned int video_width; + unsigned int video_height; + int64_t video_pts; + uint32_t palette[256]; + int has_palette; + int changed; + + unsigned int audio_bits; + unsigned int audio_channels; + unsigned int audio_sample_rate; + enum AVCodecID audio_type; + unsigned int audio_frame_count; + + int video_stream_index; + int audio_stream_index; + + int64_t audio_chunk_offset; + int audio_chunk_size; + int64_t video_chunk_offset; + int video_chunk_size; + int64_t decode_map_chunk_offset; + int decode_map_chunk_size; + + int64_t next_chunk_offset; + +} IPMVEContext; + +static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb, + AVPacket *pkt) { + + int chunk_type; + + if (s->audio_chunk_offset && s->audio_channels && s->audio_bits) { + if (s->audio_type == AV_CODEC_ID_NONE) { + av_log(NULL, AV_LOG_ERROR, "Can not read audio packet before" + "audio codec is known\n"); + return CHUNK_BAD; + } + + /* adjust for PCM audio by skipping chunk header */ + if (s->audio_type != AV_CODEC_ID_INTERPLAY_DPCM) { + s->audio_chunk_offset += 6; + s->audio_chunk_size -= 6; + } + + avio_seek(pb, s->audio_chunk_offset, SEEK_SET); + s->audio_chunk_offset = 0; + + if (s->audio_chunk_size != av_get_packet(pb, pkt, s->audio_chunk_size)) + return CHUNK_EOF; + + pkt->stream_index = s->audio_stream_index; + pkt->pts = s->audio_frame_count; + + /* audio frame maintenance */ + if (s->audio_type != AV_CODEC_ID_INTERPLAY_DPCM) + s->audio_frame_count += + (s->audio_chunk_size / s->audio_channels / (s->audio_bits / 8)); + else + s->audio_frame_count += + (s->audio_chunk_size - 6 - s->audio_channels) / s->audio_channels; + + av_dlog(NULL, "sending audio frame with pts %"PRId64" (%d audio frames)\n", + pkt->pts, s->audio_frame_count); + + chunk_type = CHUNK_VIDEO; + + } else if (s->decode_map_chunk_offset) { + + /* send both the decode map and the video data together */ + + if (av_new_packet(pkt, s->decode_map_chunk_size + s->video_chunk_size)) + return CHUNK_NOMEM; + + if (s->has_palette) { + uint8_t *pal; + + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, + AVPALETTE_SIZE); + if (pal) { + memcpy(pal, s->palette, AVPALETTE_SIZE); + s->has_palette = 0; + } + } + + if (s->changed) { + ff_add_param_change(pkt, 0, 0, 0, s->video_width, s->video_height); + s->changed = 0; + } + pkt->pos= s->decode_map_chunk_offset; + avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET); + s->decode_map_chunk_offset = 0; + + if (avio_read(pb, pkt->data, s->decode_map_chunk_size) != + s->decode_map_chunk_size) { + av_free_packet(pkt); + return CHUNK_EOF; + } + + avio_seek(pb, s->video_chunk_offset, SEEK_SET); + s->video_chunk_offset = 0; + + if (avio_read(pb, pkt->data + s->decode_map_chunk_size, + s->video_chunk_size) != s->video_chunk_size) { + av_free_packet(pkt); + return CHUNK_EOF; + } + + pkt->stream_index = s->video_stream_index; + pkt->pts = s->video_pts; + + av_dlog(NULL, "sending video frame with pts %"PRId64"\n", pkt->pts); + + s->video_pts += s->frame_pts_inc; + + chunk_type = CHUNK_VIDEO; + + } else { + + avio_seek(pb, s->next_chunk_offset, SEEK_SET); + chunk_type = CHUNK_DONE; + + } + + return chunk_type; +} + +/* This function loads and processes a single chunk in an IP movie file. + * It returns the type of chunk that was processed. */ +static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb, + AVPacket *pkt) +{ + unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; + int chunk_type; + int chunk_size; + unsigned char opcode_preamble[OPCODE_PREAMBLE_SIZE]; + unsigned char opcode_type; + unsigned char opcode_version; + int opcode_size; + unsigned char scratch[1024]; + int i, j; + int first_color, last_color; + int audio_flags; + unsigned char r, g, b; + unsigned int width, height; + + /* see if there are any pending packets */ + chunk_type = load_ipmovie_packet(s, pb, pkt); + if (chunk_type != CHUNK_DONE) + return chunk_type; + + /* read the next chunk, wherever the file happens to be pointing */ + if (url_feof(pb)) + return CHUNK_EOF; + if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != + CHUNK_PREAMBLE_SIZE) + return CHUNK_BAD; + chunk_size = AV_RL16(&chunk_preamble[0]); + chunk_type = AV_RL16(&chunk_preamble[2]); + + av_dlog(NULL, "chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size); + + switch (chunk_type) { + + case CHUNK_INIT_AUDIO: + av_dlog(NULL, "initialize audio\n"); + break; + + case CHUNK_AUDIO_ONLY: + av_dlog(NULL, "audio only\n"); + break; + + case CHUNK_INIT_VIDEO: + av_dlog(NULL, "initialize video\n"); + break; + + case CHUNK_VIDEO: + av_dlog(NULL, "video (and audio)\n"); + break; + + case CHUNK_SHUTDOWN: + av_dlog(NULL, "shutdown\n"); + break; + + case CHUNK_END: + av_dlog(NULL, "end\n"); + break; + + default: + av_dlog(NULL, "invalid chunk\n"); + chunk_type = CHUNK_BAD; + break; + + } + + while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { + + /* read the next chunk, wherever the file happens to be pointing */ + if (url_feof(pb)) { + chunk_type = CHUNK_EOF; + break; + } + if (avio_read(pb, opcode_preamble, CHUNK_PREAMBLE_SIZE) != + CHUNK_PREAMBLE_SIZE) { + chunk_type = CHUNK_BAD; + break; + } + + opcode_size = AV_RL16(&opcode_preamble[0]); + opcode_type = opcode_preamble[2]; + opcode_version = opcode_preamble[3]; + + chunk_size -= OPCODE_PREAMBLE_SIZE; + chunk_size -= opcode_size; + if (chunk_size < 0) { + av_dlog(NULL, "chunk_size countdown just went negative\n"); + chunk_type = CHUNK_BAD; + break; + } + + av_dlog(NULL, " opcode type %02X, version %d, 0x%04X bytes: ", + opcode_type, opcode_version, opcode_size); + switch (opcode_type) { + + case OPCODE_END_OF_STREAM: + av_dlog(NULL, "end of stream\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_END_OF_CHUNK: + av_dlog(NULL, "end of chunk\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_CREATE_TIMER: + av_dlog(NULL, "create timer\n"); + if ((opcode_version > 0) || (opcode_size > 6)) { + av_dlog(NULL, "bad create_timer opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (avio_read(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + s->frame_pts_inc = ((uint64_t)AV_RL32(&scratch[0])) * AV_RL16(&scratch[4]); + av_dlog(NULL, " %.2f frames/second (timer div = %d, subdiv = %d)\n", + 1000000.0 / s->frame_pts_inc, AV_RL32(&scratch[0]), + AV_RL16(&scratch[4])); + break; + + case OPCODE_INIT_AUDIO_BUFFERS: + av_dlog(NULL, "initialize audio buffers\n"); + if ((opcode_version > 1) || (opcode_size > 10)) { + av_dlog(NULL, "bad init_audio_buffers opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (avio_read(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + s->audio_sample_rate = AV_RL16(&scratch[4]); + audio_flags = AV_RL16(&scratch[2]); + /* bit 0 of the flags: 0 = mono, 1 = stereo */ + s->audio_channels = (audio_flags & 1) + 1; + /* bit 1 of the flags: 0 = 8 bit, 1 = 16 bit */ + s->audio_bits = (((audio_flags >> 1) & 1) + 1) * 8; + /* bit 2 indicates compressed audio in version 1 opcode */ + if ((opcode_version == 1) && (audio_flags & 0x4)) + s->audio_type = AV_CODEC_ID_INTERPLAY_DPCM; + else if (s->audio_bits == 16) + s->audio_type = AV_CODEC_ID_PCM_S16LE; + else + s->audio_type = AV_CODEC_ID_PCM_U8; + av_dlog(NULL, "audio: %d bits, %d Hz, %s, %s format\n", + s->audio_bits, s->audio_sample_rate, + (s->audio_channels == 2) ? "stereo" : "mono", + (s->audio_type == AV_CODEC_ID_INTERPLAY_DPCM) ? + "Interplay audio" : "PCM"); + break; + + case OPCODE_START_STOP_AUDIO: + av_dlog(NULL, "start/stop audio\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_INIT_VIDEO_BUFFERS: + av_dlog(NULL, "initialize video buffers\n"); + if ((opcode_version > 2) || (opcode_size > 8)) { + av_dlog(NULL, "bad init_video_buffers opcode\n"); + chunk_type = CHUNK_BAD; + break; + } + if (avio_read(pb, scratch, opcode_size) != + opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + width = AV_RL16(&scratch[0]) * 8; + height = AV_RL16(&scratch[2]) * 8; + if (width != s->video_width) { + s->video_width = width; + s->changed++; + } + if (height != s->video_height) { + s->video_height = height; + s->changed++; + } + if (opcode_version < 2 || !AV_RL16(&scratch[6])) { + s->video_bpp = 8; + } else { + s->video_bpp = 16; + } + av_dlog(NULL, "video resolution: %d x %d\n", + s->video_width, s->video_height); + break; + + case OPCODE_UNKNOWN_06: + case OPCODE_UNKNOWN_0E: + case OPCODE_UNKNOWN_10: + case OPCODE_UNKNOWN_12: + case OPCODE_UNKNOWN_13: + case OPCODE_UNKNOWN_14: + case OPCODE_UNKNOWN_15: + av_dlog(NULL, "unknown (but documented) opcode %02X\n", opcode_type); + avio_skip(pb, opcode_size); + break; + + case OPCODE_SEND_BUFFER: + av_dlog(NULL, "send buffer\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_AUDIO_FRAME: + av_dlog(NULL, "audio frame\n"); + + /* log position and move on for now */ + s->audio_chunk_offset = avio_tell(pb); + s->audio_chunk_size = opcode_size; + avio_skip(pb, opcode_size); + break; + + case OPCODE_SILENCE_FRAME: + av_dlog(NULL, "silence frame\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_INIT_VIDEO_MODE: + av_dlog(NULL, "initialize video mode\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_CREATE_GRADIENT: + av_dlog(NULL, "create gradient\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_SET_PALETTE: + av_dlog(NULL, "set palette\n"); + /* check for the logical maximum palette size + * (3 * 256 + 4 bytes) */ + if (opcode_size > 0x304) { + av_dlog(NULL, "demux_ipmovie: set_palette opcode too large\n"); + chunk_type = CHUNK_BAD; + break; + } + if (avio_read(pb, scratch, opcode_size) != opcode_size) { + chunk_type = CHUNK_BAD; + break; + } + + /* load the palette into internal data structure */ + first_color = AV_RL16(&scratch[0]); + last_color = first_color + AV_RL16(&scratch[2]) - 1; + /* sanity check (since they are 16 bit values) */ + if ((first_color > 0xFF) || (last_color > 0xFF)) { + av_dlog(NULL, "demux_ipmovie: set_palette indexes out of range (%d -> %d)\n", + first_color, last_color); + chunk_type = CHUNK_BAD; + break; + } + j = 4; /* offset of first palette data */ + for (i = first_color; i <= last_color; i++) { + /* the palette is stored as a 6-bit VGA palette, thus each + * component is shifted up to a 8-bit range */ + r = scratch[j++] * 4; + g = scratch[j++] * 4; + b = scratch[j++] * 4; + s->palette[i] = (0xFFU << 24) | (r << 16) | (g << 8) | (b); + s->palette[i] |= s->palette[i] >> 6 & 0x30303; + } + s->has_palette = 1; + break; + + case OPCODE_SET_PALETTE_COMPRESSED: + av_dlog(NULL, "set palette compressed\n"); + avio_skip(pb, opcode_size); + break; + + case OPCODE_SET_DECODING_MAP: + av_dlog(NULL, "set decoding map\n"); + + /* log position and move on for now */ + s->decode_map_chunk_offset = avio_tell(pb); + s->decode_map_chunk_size = opcode_size; + avio_skip(pb, opcode_size); + break; + + case OPCODE_VIDEO_DATA: + av_dlog(NULL, "set video data\n"); + + /* log position and move on for now */ + s->video_chunk_offset = avio_tell(pb); + s->video_chunk_size = opcode_size; + avio_skip(pb, opcode_size); + break; + + default: + av_dlog(NULL, "*** unknown opcode type\n"); + chunk_type = CHUNK_BAD; + break; + + } + } + + /* make a note of where the stream is sitting */ + s->next_chunk_offset = avio_tell(pb); + + /* dispatch the first of any pending packets */ + if ((chunk_type == CHUNK_VIDEO) || (chunk_type == CHUNK_AUDIO_ONLY)) + chunk_type = load_ipmovie_packet(s, pb, pkt); + + return chunk_type; +} + +static const char signature[] = "Interplay MVE File\x1A\0\x1A"; + +static int ipmovie_probe(AVProbeData *p) +{ + const uint8_t *b = p->buf; + const uint8_t *b_end = p->buf + p->buf_size - sizeof(signature); + do { + if (b[0] == signature[0] && memcmp(b, signature, sizeof(signature)) == 0) + return AVPROBE_SCORE_MAX; + b++; + } while (b < b_end); + + return 0; +} + +static int ipmovie_read_header(AVFormatContext *s) +{ + IPMVEContext *ipmovie = s->priv_data; + AVIOContext *pb = s->pb; + AVPacket pkt; + AVStream *st; + unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; + int chunk_type, i; + uint8_t signature_buffer[sizeof(signature)]; + + avio_read(pb, signature_buffer, sizeof(signature_buffer)); + while (memcmp(signature_buffer, signature, sizeof(signature))) { + memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1); + signature_buffer[sizeof(signature_buffer) - 1] = avio_r8(pb); + if (url_feof(pb)) + return AVERROR_EOF; + } + /* initialize private context members */ + ipmovie->video_pts = ipmovie->audio_frame_count = 0; + ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset = + ipmovie->decode_map_chunk_offset = 0; + + /* on the first read, this will position the stream at the first chunk */ + ipmovie->next_chunk_offset = avio_tell(pb) + 4; + + for (i = 0; i < 256; i++) + ipmovie->palette[i] = 0xFFU << 24; + + /* process the first chunk which should be CHUNK_INIT_VIDEO */ + if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO) + return AVERROR_INVALIDDATA; + + /* peek ahead to the next chunk-- if it is an init audio chunk, process + * it; if it is the first video chunk, this is a silent file */ + if (avio_read(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) != + CHUNK_PREAMBLE_SIZE) + return AVERROR(EIO); + chunk_type = AV_RL16(&chunk_preamble[2]); + avio_seek(pb, -CHUNK_PREAMBLE_SIZE, SEEK_CUR); + + if (chunk_type == CHUNK_VIDEO) + ipmovie->audio_type = AV_CODEC_ID_NONE; /* no audio */ + else if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_AUDIO) + return AVERROR_INVALIDDATA; + + /* initialize the stream decoders */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 63, 1, 1000000); + ipmovie->video_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_INTERPLAY_VIDEO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = ipmovie->video_width; + st->codec->height = ipmovie->video_height; + st->codec->bits_per_coded_sample = ipmovie->video_bpp; + + if (ipmovie->audio_type) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 32, 1, ipmovie->audio_sample_rate); + ipmovie->audio_stream_index = st->index; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = ipmovie->audio_type; + st->codec->codec_tag = 0; /* no tag */ + st->codec->channels = ipmovie->audio_channels; + st->codec->channel_layout = st->codec->channels == 1 ? AV_CH_LAYOUT_MONO : + AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = ipmovie->audio_sample_rate; + st->codec->bits_per_coded_sample = ipmovie->audio_bits; + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * + st->codec->bits_per_coded_sample; + if (st->codec->codec_id == AV_CODEC_ID_INTERPLAY_DPCM) + st->codec->bit_rate /= 2; + st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; + } + + return 0; +} + +static int ipmovie_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + IPMVEContext *ipmovie = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + + for (;;) { + ret = process_ipmovie_chunk(ipmovie, pb, pkt); + if (ret == CHUNK_BAD) + ret = AVERROR_INVALIDDATA; + else if (ret == CHUNK_EOF) + ret = AVERROR(EIO); + else if (ret == CHUNK_NOMEM) + ret = AVERROR(ENOMEM); + else if (ret == CHUNK_VIDEO) + ret = 0; + else if (ret == CHUNK_INIT_VIDEO || ret == CHUNK_INIT_AUDIO) + continue; + else + ret = -1; + + return ret; + } +} + +AVInputFormat ff_ipmovie_demuxer = { + .name = "ipmovie", + .long_name = NULL_IF_CONFIG_SMALL("Interplay MVE"), + .priv_data_size = sizeof(IPMVEContext), + .read_probe = ipmovie_probe, + .read_header = ipmovie_read_header, + .read_packet = ipmovie_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ipmovie.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ipmovie.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/ipmovie.o libavformat/ipmovie.o: libavformat/ipmovie.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ipmovie.o Binary file ffmpeg/libavformat/ipmovie.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircam.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircam.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,47 @@ +/* + * IRCAM common code + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +const AVCodecTag ff_codec_ircam_le_tags[] = { + { AV_CODEC_ID_PCM_ALAW, 0x10001 }, + { AV_CODEC_ID_PCM_F32LE, 0x00004 }, + { AV_CODEC_ID_PCM_F64LE, 0x00008 }, + { AV_CODEC_ID_PCM_MULAW, 0x20001 }, + { AV_CODEC_ID_PCM_S16LE, 0x00002 }, + { AV_CODEC_ID_PCM_S24LE, 0x00003 }, + { AV_CODEC_ID_PCM_S32LE, 0x40004 }, + { AV_CODEC_ID_PCM_S8, 0x00001 }, + { AV_CODEC_ID_NONE, 0 }, +}; + +const AVCodecTag ff_codec_ircam_be_tags[] = { + { AV_CODEC_ID_PCM_ALAW, 0x10001 }, + { AV_CODEC_ID_PCM_F32BE, 0x00004 }, + { AV_CODEC_ID_PCM_F64BE, 0x00008 }, + { AV_CODEC_ID_PCM_MULAW, 0x20001 }, + { AV_CODEC_ID_PCM_S16BE, 0x00002 }, + { AV_CODEC_ID_PCM_S24BE, 0x00003 }, + { AV_CODEC_ID_PCM_S32BE, 0x40004 }, + { AV_CODEC_ID_PCM_S8, 0x00001 }, + { AV_CODEC_ID_NONE, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircam.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircam.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/ircam.o libavformat/ircam.o: libavformat/ircam.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircam.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircam.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,30 @@ +/* + * IRCAM common code + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_IRCAM_H +#define AVFORMAT_IRCAM_H + +#include "internal.h" + +extern const AVCodecTag ff_codec_ircam_be_tags[]; +extern const AVCodecTag ff_codec_ircam_le_tags[]; + +#endif /* AVFORMAT_IRCAM_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircam.o Binary file ffmpeg/libavformat/ircam.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircamdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,115 @@ +/* + * IRCAM demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" +#include "ircam.h" + +static int ircam_probe(AVProbeData *p) +{ + if ((p->buf[0] == 0x64 && p->buf[1] == 0xA3 && p->buf[3] == 0x00 && + p->buf[2] >= 1 && p->buf[2] <= 4) || + (p->buf[3] == 0x64 && p->buf[2] == 0xA3 && p->buf[0] == 0x00 && + p->buf[1] >= 1 && p->buf[1] <= 3) && + AV_RN32(p->buf + 4) && AV_RN32(p->buf + 8)) + return AVPROBE_SCORE_MAX / 4 * 3; + return 0; +} + +static const struct endianess { + uint32_t magic; + int is_le; +} table[] = { + { 0x64A30100, 0 }, + { 0x64A30200, 1 }, + { 0x64A30300, 0 }, + { 0x64A30400, 1 }, + { 0x0001A364, 1 }, + { 0x0002A364, 0 }, + { 0x0003A364, 1 }, +}; + +static int ircam_read_header(AVFormatContext *s) +{ + uint32_t magic, sample_rate, channels, tag; + const AVCodecTag *tags; + int le = -1, i; + AVStream *st; + + magic = avio_rl32(s->pb); + for (i = 0; i < 7; i++) { + if (magic == table[i].magic) { + le = table[i].is_le; + break; + } + } + + if (le == 1) { + sample_rate = av_int2float(avio_rl32(s->pb)); + channels = avio_rl32(s->pb); + tag = avio_rl32(s->pb); + tags = ff_codec_ircam_le_tags; + } else if (le == 0) { + sample_rate = av_int2float(avio_rb32(s->pb)); + channels = avio_rb32(s->pb); + tag = avio_rb32(s->pb); + tags = ff_codec_ircam_be_tags; + } else { + return AVERROR_INVALIDDATA; + } + + if (!channels || !sample_rate) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + + st->codec->codec_id = ff_codec_get_id(tags, tag); + if (st->codec->codec_id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_ERROR, "unknown tag %X\n", tag); + return AVERROR_INVALIDDATA; + } + + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + avio_skip(s->pb, 1008); + + return 0; +} + +AVInputFormat ff_ircam_demuxer = { + .name = "ircam", + .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), + .read_probe = ircam_probe, + .read_header = ircam_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "sf,ircam", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircamdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/ircamdec.o libavformat/ircamdec.o: libavformat/ircamdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h libavformat/ircam.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamdec.o Binary file ffmpeg/libavformat/ircamdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircamenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,62 @@ +/* + * IRCAM muxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "rawenc.h" +#include "ircam.h" + +static int ircam_write_header(AVFormatContext *s) +{ + AVCodecContext *codec = s->streams[0]->codec; + uint32_t tag; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + return AVERROR(EINVAL); + } + + tag = ff_codec_get_tag(ff_codec_ircam_le_tags, codec->codec_id); + if (!tag) { + av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + return AVERROR(EINVAL); + } + + avio_wl32(s->pb, 0x0001A364); + avio_wl32(s->pb, av_float2int(codec->sample_rate)); + avio_wl32(s->pb, codec->channels); + avio_wl32(s->pb, tag); + ffio_fill(s->pb, 0, 1008); + return 0; +} + +AVOutputFormat ff_ircam_muxer = { + .name = "ircam", + .extensions = "sf,ircam", + .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_NONE, + .write_header = ircam_write_header, + .write_packet = ff_raw_write_packet, + .codec_tag = (const AVCodecTag *const []){ ff_codec_ircam_le_tags, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ircamenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/ircamenc.o libavformat/ircamenc.o: libavformat/ircamenc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h libavformat/rawenc.h \ + libavformat/ircam.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ircamenc.o Binary file ffmpeg/libavformat/ircamenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/isom.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/isom.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,572 @@ +/* + * ISO Media common code + * Copyright (c) 2001 Fabrice Bellard + * Copyright (c) 2002 Francois Revol + * Copyright (c) 2006 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//#define DEBUG + +#include "avformat.h" +#include "internal.h" +#include "isom.h" +#include "libavcodec/mpeg4audio.h" +#include "libavcodec/mpegaudiodata.h" + +/* http://www.mp4ra.org */ +/* ordered by muxing preference */ +const AVCodecTag ff_mp4_obj_type[] = { + { AV_CODEC_ID_MOV_TEXT , 0x08 }, + { AV_CODEC_ID_MPEG4 , 0x20 }, + { AV_CODEC_ID_H264 , 0x21 }, + { AV_CODEC_ID_AAC , 0x40 }, + { AV_CODEC_ID_MP4ALS , 0x40 }, /* 14496-3 ALS */ + { AV_CODEC_ID_MPEG2VIDEO , 0x61 }, /* MPEG2 Main */ + { AV_CODEC_ID_MPEG2VIDEO , 0x60 }, /* MPEG2 Simple */ + { AV_CODEC_ID_MPEG2VIDEO , 0x62 }, /* MPEG2 SNR */ + { AV_CODEC_ID_MPEG2VIDEO , 0x63 }, /* MPEG2 Spatial */ + { AV_CODEC_ID_MPEG2VIDEO , 0x64 }, /* MPEG2 High */ + { AV_CODEC_ID_MPEG2VIDEO , 0x65 }, /* MPEG2 422 */ + { AV_CODEC_ID_AAC , 0x66 }, /* MPEG2 AAC Main */ + { AV_CODEC_ID_AAC , 0x67 }, /* MPEG2 AAC Low */ + { AV_CODEC_ID_AAC , 0x68 }, /* MPEG2 AAC SSR */ + { AV_CODEC_ID_MP3 , 0x69 }, /* 13818-3 */ + { AV_CODEC_ID_MP2 , 0x69 }, /* 11172-3 */ + { AV_CODEC_ID_MPEG1VIDEO , 0x6A }, /* 11172-2 */ + { AV_CODEC_ID_MP3 , 0x6B }, /* 11172-3 */ + { AV_CODEC_ID_MJPEG , 0x6C }, /* 10918-1 */ + { AV_CODEC_ID_PNG , 0x6D }, + { AV_CODEC_ID_JPEG2000 , 0x6E }, /* 15444-1 */ + { AV_CODEC_ID_VC1 , 0xA3 }, + { AV_CODEC_ID_DIRAC , 0xA4 }, + { AV_CODEC_ID_AC3 , 0xA5 }, + { AV_CODEC_ID_DTS , 0xA9 }, /* mp4ra.org */ + { AV_CODEC_ID_VORBIS , 0xDD }, /* non standard, gpac uses it */ + { AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* non standard, see unsupported-embedded-subs-2.mp4 */ + { AV_CODEC_ID_QCELP , 0xE1 }, + { AV_CODEC_ID_MPEG4SYSTEMS, 0x01 }, + { AV_CODEC_ID_MPEG4SYSTEMS, 0x02 }, + { AV_CODEC_ID_NONE , 0 }, +}; + +const AVCodecTag ff_codec_movvideo_tags[] = { +/* { AV_CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */ + + { AV_CODEC_ID_RAWVIDEO, MKTAG('r', 'a', 'w', ' ') }, /* Uncompressed RGB */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', '2') }, /* Uncompressed YUV422 */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('2', 'v', 'u', 'y') }, /* UNCOMPRESSED 8BIT 4:2:2 */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('y', 'u', 'v', 's') }, /* same as 2vuy but byte swapped */ + + { AV_CODEC_ID_RAWVIDEO, MKTAG('L', '5', '5', '5') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('L', '5', '6', '5') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '5', '6', '5') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('2', '4', 'B', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '1', '6', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', '4', '8', 'r') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'b', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'r', 'g') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('b', 'x', 'y', 'v') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('N', 'O', '1', '6') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('D', 'V', 'O', 'O') }, /* Digital Voodoo SD 8 Bit */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '2', '0') }, /* Radius DV YUV PAL */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', '1', '1') }, /* Radius DV YUV NTSC */ + + { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'k') }, /* UNCOMPRESSED 10BIT RGB */ + { AV_CODEC_ID_R10K, MKTAG('R', '1', '0', 'g') }, /* UNCOMPRESSED 10BIT RGB */ + { AV_CODEC_ID_R210, MKTAG('r', '2', '1', '0') }, /* UNCOMPRESSED 10BIT RGB */ + { AV_CODEC_ID_AVUI, MKTAG('A', 'V', 'U', 'I') }, /* AVID Uncompressed deinterleaved UYVY422 */ + { AV_CODEC_ID_AVRP, MKTAG('A', 'V', 'r', 'p') }, /* Avid 1:1 10-bit RGB Packer */ + { AV_CODEC_ID_AVRP, MKTAG('S', 'U', 'D', 'S') }, /* Avid DS Uncompressed */ + { AV_CODEC_ID_V210, MKTAG('v', '2', '1', '0') }, /* UNCOMPRESSED 10BIT 4:2:2 */ + { AV_CODEC_ID_V210, MKTAG('b', 'x', 'y', '2') }, /* BOXX 10BIT 4:2:2 */ + { AV_CODEC_ID_V308, MKTAG('v', '3', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4 */ + { AV_CODEC_ID_V408, MKTAG('v', '4', '0', '8') }, /* UNCOMPRESSED 8BIT 4:4:4:4 */ + { AV_CODEC_ID_V410, MKTAG('v', '4', '1', '0') }, /* UNCOMPRESSED 10BIT 4:4:4 */ + { AV_CODEC_ID_Y41P, MKTAG('Y', '4', '1', 'P') }, /* UNCOMPRESSED 12BIT 4:1:1 */ + { AV_CODEC_ID_YUV4, MKTAG('y', 'u', 'v', '4') }, /* libquicktime packed yuv420p */ + { AV_CODEC_ID_TARGA_Y216, MKTAG('Y', '2', '1', '6') }, + + { AV_CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ + { AV_CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ + { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) */ +/* { AV_CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, *//* MJPEG with alpha-channel (AVID ABVB/Truevision NuVista) */ + { AV_CODEC_ID_MJPEG, MKTAG('d', 'm', 'b', '1') }, /* Motion JPEG OpenDML */ + { AV_CODEC_ID_MJPEGB, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */ + + { AV_CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, /* Sorenson Video v1 */ + { AV_CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, /* Sorenson Video v1 */ + { AV_CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', 'i') }, /* Sorenson Video v1 (from QT specs)*/ + { AV_CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, /* Sorenson Video v3 */ + + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ + { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, + { AV_CODEC_ID_MPEG4, MKTAG('3', 'I', 'V', '2') }, /* experimental: 3IVX files before ivx D4 4.5.1 */ + + { AV_CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, /* H263 */ + { AV_CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, /* H263 ?? works */ + + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'p', 'p') }, /* DVCPRO PAL produced by FCP */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'p') }, /* DVCPRO50 PAL produced by FCP */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'n') }, /* DVCPRO50 NTSC produced by FCP */ + { AV_CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', 'v') }, /* AVID DV */ + { AV_CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', '1') }, /* AVID DV100 */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'q') }, /* DVCPRO HD 720p50 */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'p') }, /* DVCPRO HD 720p60 */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '1') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '2') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '4') }, + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '5') }, /* DVCPRO HD 50i produced by FCP */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '6') }, /* DVCPRO HD 60i produced by FCP */ + { AV_CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', '3') }, /* DVCPRO HD 30p produced by FCP */ + + { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, /* On2 VP3 */ + { AV_CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, /* Apple Video (RPZA) */ + { AV_CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') }, /* Cinepak */ + { AV_CODEC_ID_8BPS, MKTAG('8', 'B', 'P', 'S') }, /* Planar RGB (8BPS) */ + { AV_CODEC_ID_SMC, MKTAG('s', 'm', 'c', ' ') }, /* Apple Graphics (SMC) */ + { AV_CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* Apple Animation (RLE) */ + { AV_CODEC_ID_SGIRLE, MKTAG('r', 'l', 'e', '1') }, /* SGI RLE 8-bit */ + { AV_CODEC_ID_MSRLE, MKTAG('W', 'R', 'L', 'E') }, + { AV_CODEC_ID_QDRAW, MKTAG('q', 'd', 'r', 'w') }, /* QuickDraw */ + + { AV_CODEC_ID_RAWVIDEO, MKTAG('W', 'R', 'A', 'W') }, + + { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'p') }, /* AVC-Intra 50M 720p24/30/60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', 'q') }, /* AVC-Intra 50M 720p25/50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '2') }, /* AVC-Intra 50M 1080p25/50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '3') }, /* AVC-Intra 50M 1080p24/30/60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '5') }, /* AVC-Intra 50M 1080i50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '5', '6') }, /* AVC-Intra 50M 1080i60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'p') }, /* AVC-Intra 100M 720p24/30/60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', 'q') }, /* AVC-Intra 100M 720p25/50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '2') }, /* AVC-Intra 100M 1080p25/50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '3') }, /* AVC-Intra 100M 1080p24/30/60 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '5') }, /* AVC-Intra 100M 1080i50 */ + { AV_CODEC_ID_H264, MKTAG('a', 'i', '1', '6') }, /* AVC-Intra 100M 1080i60 */ + { AV_CODEC_ID_H264, MKTAG('A', 'V', 'i', 'n') }, /* AVC-Intra with implicit SPS/PPS */ + + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', '1') }, /* Apple MPEG-1 Camcorder */ + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', '1', 'v', ' ') }, + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', '2', 'v', '1') }, /* Apple MPEG-2 Camcorder */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '1') }, /* MPEG2 HDV 720p30 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '2') }, /* MPEG2 HDV 1080i60 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '3') }, /* MPEG2 HDV 1080i50 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '4') }, /* MPEG2 HDV 720p24 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '5') }, /* MPEG2 HDV 720p25 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '6') }, /* MPEG2 HDV 1080p24 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '7') }, /* MPEG2 HDV 1080p25 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '8') }, /* MPEG2 HDV 1080p30 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', '9') }, /* MPEG2 HDV 720p60 JVC */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('h', 'd', 'v', 'a') }, /* MPEG2 HDV 720p50 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '5', 'n') }, /* MPEG2 IMX NTSC 525/60 50mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '5', 'p') }, /* MPEG2 IMX PAL 625/50 50mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '4', 'n') }, /* MPEG2 IMX NTSC 525/60 40mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '4', 'p') }, /* MPEG2 IMX PAL 625/50 40mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'n') }, /* MPEG2 IMX NTSC 525/60 30mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'x', '3', 'p') }, /* MPEG2 IMX PAL 625/50 30mb/s produced by FCP */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '4') }, /* XDCAM HD422 720p24 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '5') }, /* XDCAM HD422 720p25 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', '9') }, /* XDCAM HD422 720p60 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'a') }, /* XDCAM HD422 720p50 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'b') }, /* XDCAM HD422 1080i60 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'c') }, /* XDCAM HD422 1080i50 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'd') }, /* XDCAM HD422 1080p24 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'e') }, /* XDCAM HD422 1080p25 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', '5', 'f') }, /* XDCAM HD422 1080p30 CBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '1') }, /* XDCAM EX 720p30 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '2') }, /* XDCAM HD 1080i60 */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '3') }, /* XDCAM HD 1080i50 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '4') }, /* XDCAM EX 720p24 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '5') }, /* XDCAM EX 720p25 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '6') }, /* XDCAM HD 1080p24 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '7') }, /* XDCAM HD 1080p25 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '8') }, /* XDCAM HD 1080p30 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', '9') }, /* XDCAM EX 720p60 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'a') }, /* XDCAM EX 720p50 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'b') }, /* XDCAM EX 1080i60 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'c') }, /* XDCAM EX 1080i50 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'd') }, /* XDCAM EX 1080p24 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'e') }, /* XDCAM EX 1080p25 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'v', 'f') }, /* XDCAM EX 1080p30 VBR */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'h', 'd') }, /* XDCAM HD 540p */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('x', 'd', 'h', '2') }, /* XDCAM HD422 540p */ + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('A', 'V', 'm', 'p') }, /* AVID IMX PAL */ + + { AV_CODEC_ID_JPEG2000, MKTAG('m', 'j', 'p', '2') }, /* JPEG 2000 produced by FCP */ + + { AV_CODEC_ID_TARGA, MKTAG('t', 'g', 'a', ' ') }, /* Truevision Targa */ + { AV_CODEC_ID_TIFF, MKTAG('t', 'i', 'f', 'f') }, /* TIFF embedded in MOV */ + { AV_CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, /* embedded gif files as frames (usually one "click to play movie" frame) */ + { AV_CODEC_ID_PNG, MKTAG('p', 'n', 'g', ' ') }, + { AV_CODEC_ID_PNG, MKTAG('M', 'N', 'G', ' ') }, + + { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, /* SMPTE RP 2025 */ + { AV_CODEC_ID_CAVS, MKTAG('a', 'v', 's', '2') }, + + { AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') }, + { AV_CODEC_ID_DNXHD, MKTAG('A', 'V', 'd', 'n') }, /* AVID DNxHD */ + { AV_CODEC_ID_H263, MKTAG('H', '2', '6', '3') }, + { AV_CODEC_ID_MSMPEG4V3, MKTAG('3', 'I', 'V', 'D') }, /* 3ivx DivX Doctor */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', '1', 'x') }, /* AVID 1:1x */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'u', 'p') }, + { AV_CODEC_ID_SGI, MKTAG('s', 'g', 'i', ' ') }, /* SGI */ + { AV_CODEC_ID_DPX, MKTAG('d', 'p', 'x', ' ') }, /* DPX */ + { AV_CODEC_ID_EXR, MKTAG('e', 'x', 'r', ' ') }, /* OpenEXR */ + + { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'h') }, /* Apple ProRes 422 High Quality */ + { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'n') }, /* Apple ProRes 422 Standard Definition */ + { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 's') }, /* Apple ProRes 422 LT */ + { AV_CODEC_ID_PRORES, MKTAG('a', 'p', 'c', 'o') }, /* Apple ProRes 422 Proxy */ + { AV_CODEC_ID_PRORES, MKTAG('a', 'p', '4', 'h') }, /* Apple ProRes 4444 */ + { AV_CODEC_ID_FLIC, MKTAG('f', 'l', 'i', 'c') }, + + { AV_CODEC_ID_NONE, 0 }, +}; + +const AVCodecTag ff_codec_movaudio_tags[] = { + { AV_CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_AC3, MKTAG('a', 'c', '-', '3') }, /* ETSI TS 102 366 Annex F */ + { AV_CODEC_ID_AC3, MKTAG('s', 'a', 'c', '3') }, /* Nero Recode */ + { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, + { AV_CODEC_ID_ALAC, MKTAG('a', 'l', 'a', 'c') }, + { AV_CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, /* AMR-NB 3gp */ + { AV_CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, /* AMR-WB 3gp */ + { AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'c') }, /* DTS formats prior to DTS-HD */ + { AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'h') }, /* DTS-HD audio formats */ + { AV_CODEC_ID_DTS, MKTAG('d', 't', 's', 'l') }, /* DTS-HD Lossless formats */ + { AV_CODEC_ID_DTS, MKTAG('D', 'T', 'S', ' ') }, /* non-standard */ + { AV_CODEC_ID_EAC3, MKTAG('e', 'c', '-', '3') }, /* ETSI TS 102 366 Annex F (only valid in ISOBMFF) */ + { AV_CODEC_ID_DVAUDIO, MKTAG('v', 'd', 'v', 'a') }, + { AV_CODEC_ID_DVAUDIO, MKTAG('d', 'v', 'c', 'a') }, + { AV_CODEC_ID_GSM, MKTAG('a', 'g', 's', 'm') }, + { AV_CODEC_ID_ILBC, MKTAG('i', 'l', 'b', 'c') }, + { AV_CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, + { AV_CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, + { AV_CODEC_ID_MP1, MKTAG('.', 'm', 'p', '1') }, + { AV_CODEC_ID_MP2, MKTAG('.', 'm', 'p', '2') }, + { AV_CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') }, + { AV_CODEC_ID_MP3, 0x6D730055 }, + { AV_CODEC_ID_NELLYMOSER, MKTAG('n', 'm', 'o', 's') }, /* Flash Media Server */ + { AV_CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, + { AV_CODEC_ID_PCM_F32BE, MKTAG('f', 'l', '3', '2') }, + { AV_CODEC_ID_PCM_F32LE, MKTAG('f', 'l', '3', '2') }, + { AV_CODEC_ID_PCM_F64BE, MKTAG('f', 'l', '6', '4') }, + { AV_CODEC_ID_PCM_F64LE, MKTAG('f', 'l', '6', '4') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, + { AV_CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, + { AV_CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') }, + { AV_CODEC_ID_PCM_S24LE, MKTAG('i', 'n', '2', '4') }, + { AV_CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2') }, + { AV_CODEC_ID_PCM_S32LE, MKTAG('i', 'n', '3', '2') }, + { AV_CODEC_ID_PCM_S8, MKTAG('s', 'o', 'w', 't') }, + { AV_CODEC_ID_PCM_U8, MKTAG('r', 'a', 'w', ' ') }, + { AV_CODEC_ID_PCM_U8, MKTAG('N', 'O', 'N', 'E') }, + { AV_CODEC_ID_QCELP, MKTAG('Q', 'c', 'l', 'p') }, + { AV_CODEC_ID_QCELP, MKTAG('Q', 'c', 'l', 'q') }, + { AV_CODEC_ID_QCELP, MKTAG('s', 'q', 'c', 'p') }, /* ISO Media fourcc */ + { AV_CODEC_ID_QDM2, MKTAG('Q', 'D', 'M', '2') }, + { AV_CODEC_ID_QDMC, MKTAG('Q', 'D', 'M', 'C') }, + { AV_CODEC_ID_SPEEX, MKTAG('s', 'p', 'e', 'x') }, /* Flash Media Server */ + { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', 'N') }, + { AV_CODEC_ID_WMAV2, MKTAG('W', 'M', 'A', '2') }, + { AV_CODEC_ID_EVRC, MKTAG('s', 'e', 'v', 'c') }, /* 3GPP2 */ + { AV_CODEC_ID_SMV, MKTAG('s', 's', 'm', 'v') }, /* 3GPP2 */ + { AV_CODEC_ID_NONE, 0 }, +}; + +const AVCodecTag ff_codec_movsubtitle_tags[] = { + { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'e', 'x', 't') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, + { AV_CODEC_ID_EIA_608, MKTAG('c', '6', '0', '8') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +/* map numeric codes from mdhd atom to ISO 639 */ +/* cf. QTFileFormat.pdf p253, qtff.pdf p205 */ +/* http://developer.apple.com/documentation/mac/Text/Text-368.html */ +/* deprecated by putting the code as 3*5bit ascii */ +static const char mov_mdhd_language_map[][4] = { + /* 0-9 */ + "eng", "fra", "ger", "ita", "dut", "sve", "spa", "dan", "por", "nor", + "heb", "jpn", "ara", "fin", "gre", "ice", "mlt", "tur", "hr "/*scr*/, "chi"/*ace?*/, + "urd", "hin", "tha", "kor", "lit", "pol", "hun", "est", "lav", "", + "fo ", "", "rus", "chi", "", "iri", "alb", "ron", "ces", "slk", + "slv", "yid", "sr ", "mac", "bul", "ukr", "bel", "uzb", "kaz", "aze", + /*?*/ + "aze", "arm", "geo", "mol", "kir", "tgk", "tuk", "mon", "", "pus", + "kur", "kas", "snd", "tib", "nep", "san", "mar", "ben", "asm", "guj", + "pa ", "ori", "mal", "kan", "tam", "tel", "", "bur", "khm", "lao", + /* roman? arabic? */ + "vie", "ind", "tgl", "may", "may", "amh", "tir", "orm", "som", "swa", + /*==rundi?*/ + "", "run", "", "mlg", "epo", "", "", "", "", "", + /* 100 */ + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "wel", "baq", + "cat", "lat", "que", "grn", "aym", "tat", "uig", "dzo", "jav" +}; + +int ff_mov_iso639_to_lang(const char lang[4], int mp4) +{ + int i, code = 0; + + /* old way, only for QT? */ + for (i = 0; lang[0] && !mp4 && i < FF_ARRAY_ELEMS(mov_mdhd_language_map); i++) { + if (!strcmp(lang, mov_mdhd_language_map[i])) + return i; + } + /* XXX:can we do that in mov too? */ + if (!mp4) + return -1; + /* handle undefined as such */ + if (lang[0] == '\0') + lang = "und"; + /* 5bit ascii */ + for (i = 0; i < 3; i++) { + uint8_t c = lang[i]; + c -= 0x60; + if (c > 0x1f) + return -1; + code <<= 5; + code |= c; + } + return code; +} + +int ff_mov_lang_to_iso639(unsigned code, char to[4]) +{ + int i; + memset(to, 0, 4); + /* is it the mangled iso code? */ + /* see http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt */ + if (code >= 0x400 && code != 0x7fff) { + for (i = 2; i >= 0; i--) { + to[i] = 0x60 + (code & 0x1f); + code >>= 5; + } + return 1; + } + /* old fashion apple lang code */ + if (code >= FF_ARRAY_ELEMS(mov_mdhd_language_map)) + return 0; + if (!mov_mdhd_language_map[code][0]) + return 0; + memcpy(to, mov_mdhd_language_map[code], 4); + return 1; +} + +int ff_mp4_read_descr_len(AVIOContext *pb) +{ + int len = 0; + int count = 4; + while (count--) { + int c = avio_r8(pb); + len = (len << 7) | (c & 0x7f); + if (!(c & 0x80)) + break; + } + return len; +} + +int ff_mp4_read_descr(AVFormatContext *fc, AVIOContext *pb, int *tag) +{ + int len; + *tag = avio_r8(pb); + len = ff_mp4_read_descr_len(pb); + av_dlog(fc, "MPEG4 description: tag=0x%02x len=%d\n", *tag, len); + return len; +} + +void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id) +{ + int flags; + if (es_id) *es_id = avio_rb16(pb); + else avio_rb16(pb); + flags = avio_r8(pb); + if (flags & 0x80) //streamDependenceFlag + avio_rb16(pb); + if (flags & 0x40) { //URL_Flag + int len = avio_r8(pb); + avio_skip(pb, len); + } + if (flags & 0x20) //OCRstreamFlag + avio_rb16(pb); +} + +static const AVCodecTag mp4_audio_types[] = { + { AV_CODEC_ID_MP3ON4, AOT_PS }, /* old mp3on4 draft */ + { AV_CODEC_ID_MP3ON4, AOT_L1 }, /* layer 1 */ + { AV_CODEC_ID_MP3ON4, AOT_L2 }, /* layer 2 */ + { AV_CODEC_ID_MP3ON4, AOT_L3 }, /* layer 3 */ + { AV_CODEC_ID_MP4ALS, AOT_ALS }, /* MPEG-4 ALS */ + { AV_CODEC_ID_NONE, AOT_NULL }, +}; + +int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb) +{ + int len, tag; + int object_type_id = avio_r8(pb); + avio_r8(pb); /* stream type */ + avio_rb24(pb); /* buffer size db */ + avio_rb32(pb); /* max bitrate */ + avio_rb32(pb); /* avg bitrate */ + + if(avcodec_is_open(st->codec)) { + av_log(fc, AV_LOG_DEBUG, "codec open in read_dec_config_descr\n"); + return -1; + } + + st->codec->codec_id= ff_codec_get_id(ff_mp4_obj_type, object_type_id); + av_dlog(fc, "esds object type id 0x%02x\n", object_type_id); + len = ff_mp4_read_descr(fc, pb, &tag); + if (tag == MP4DecSpecificDescrTag) { + av_dlog(fc, "Specific MPEG4 header len=%d\n", len); + if (!len || (uint64_t)len > (1<<30)) + return -1; + av_free(st->codec->extradata); + st->codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + avio_read(pb, st->codec->extradata, len); + st->codec->extradata_size = len; + if (st->codec->codec_id == AV_CODEC_ID_AAC) { + MPEG4AudioConfig cfg; + avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata, + st->codec->extradata_size * 8, 1); + st->codec->channels = cfg.channels; + if (cfg.object_type == 29 && cfg.sampling_index < 3) // old mp3on4 + st->codec->sample_rate = avpriv_mpa_freq_tab[cfg.sampling_index]; + else if (cfg.ext_sample_rate) + st->codec->sample_rate = cfg.ext_sample_rate; + else + st->codec->sample_rate = cfg.sample_rate; + av_dlog(fc, "mp4a config channels %d obj %d ext obj %d " + "sample rate %d ext sample rate %d\n", st->codec->channels, + cfg.object_type, cfg.ext_object_type, + cfg.sample_rate, cfg.ext_sample_rate); + if (!(st->codec->codec_id = ff_codec_get_id(mp4_audio_types, + cfg.object_type))) + st->codec->codec_id = AV_CODEC_ID_AAC; + } + } + return 0; +} + +typedef struct MovChannelLayout { + int64_t channel_layout; + uint32_t layout_tag; +} MovChannelLayout; + +static const MovChannelLayout mov_channel_layout[] = { + { AV_CH_LAYOUT_MONO, (100<<16) | 1}, // kCAFChannelLayoutTag_Mono + { AV_CH_LAYOUT_STEREO, (101<<16) | 2}, // kCAFChannelLayoutTag_Stereo + { AV_CH_LAYOUT_STEREO, (102<<16) | 2}, // kCAFChannelLayoutTag_StereoHeadphones + { AV_CH_LAYOUT_2_1, (131<<16) | 3}, // kCAFChannelLayoutTag_ITU_2_1 + { AV_CH_LAYOUT_QUAD, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_2_2, (132<<16) | 4}, // kCAFChannelLayoutTag_ITU_2_2 + { AV_CH_LAYOUT_QUAD, (108<<16) | 4}, // kCAFChannelLayoutTag_Quadraphonic + { AV_CH_LAYOUT_SURROUND, (113<<16) | 3}, // kCAFChannelLayoutTag_MPEG_3_0_A + { AV_CH_LAYOUT_4POINT0, (115<<16) | 4}, // kCAFChannelLayoutTag_MPEG_4_0_A + { AV_CH_LAYOUT_5POINT0_BACK, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT0, (117<<16) | 5}, // kCAFChannelLayoutTag_MPEG_5_0_A + { AV_CH_LAYOUT_5POINT1_BACK, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_5POINT1, (121<<16) | 6}, // kCAFChannelLayoutTag_MPEG_5_1_A + { AV_CH_LAYOUT_7POINT1, (128<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_C + { AV_CH_LAYOUT_7POINT1_WIDE, (126<<16) | 8}, // kCAFChannelLayoutTag_MPEG_7_1_A + { AV_CH_LAYOUT_5POINT1_BACK|AV_CH_LAYOUT_STEREO_DOWNMIX, (130<<16) | 8}, // kCAFChannelLayoutTag_SMPTE_DTV + { AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY, (133<<16) | 3}, // kCAFChannelLayoutTag_DVD_4 + { AV_CH_LAYOUT_2_1|AV_CH_LOW_FREQUENCY, (134<<16) | 4}, // kCAFChannelLayoutTag_DVD_5 + { AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY, (135<<16) | 4}, // kCAFChannelLayoutTag_DVD_6 + { AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY, (136<<16) | 4}, // kCAFChannelLayoutTag_DVD_10 + { AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY, (137<<16) | 5}, // kCAFChannelLayoutTag_DVD_11 + { 0, 0}, +}; +#if 0 +int ff_mov_read_chan(AVFormatContext *s, AVStream *st, int64_t size) +{ + AVCodecContext *codec= st->codec; + uint32_t layout_tag; + AVIOContext *pb = s->pb; + const MovChannelLayout *layouts = mov_channel_layout; + + if (size < 12) + return AVERROR_INVALIDDATA; + + layout_tag = avio_rb32(pb); + size -= 4; + if (layout_tag == 0) { // kCAFChannelLayoutTag_UseChannelDescriptions + // Channel descriptions not implemented + av_log_ask_for_sample(s, "Unimplemented container channel layout.\n"); + avio_skip(pb, size); + return 0; + } + if (layout_tag == 0x10000) { // kCAFChannelLayoutTag_UseChannelBitmap + codec->channel_layout = avio_rb32(pb); + size -= 4; + avio_skip(pb, size); + return 0; + } + while (layouts->channel_layout) { + if (layout_tag == layouts->layout_tag) { + codec->channel_layout = layouts->channel_layout; + break; + } + layouts++; + } + if (!codec->channel_layout) + av_log(s, AV_LOG_WARNING, "Unknown container channel layout.\n"); + avio_skip(pb, size); + + return 0; +} +#endif + +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout) +{ + const MovChannelLayout *layouts; + uint32_t layout_tag = 0; + + for (layouts = mov_channel_layout; layouts->channel_layout; layouts++) + if (channel_layout == layouts->channel_layout) { + layout_tag = layouts->layout_tag; + break; + } + + if (layout_tag) { + avio_wb32(pb, layout_tag); // mChannelLayoutTag + avio_wb32(pb, 0); // mChannelBitmap + } else { + avio_wb32(pb, 0x10000); // kCAFChannelLayoutTag_UseChannelBitmap + avio_wb32(pb, channel_layout); + } + avio_wb32(pb, 0); // mNumberChannelDescriptions +} + diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/isom.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/isom.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +libavformat/isom.o libavformat/isom.o: libavformat/isom.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/isom.h libavformat/dv.h libavcodec/mpeg4audio.h \ + libavcodec/get_bits.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavcodec/put_bits.h libavutil/bswap.h \ + libavcodec/mpegaudiodata.h libavutil/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/isom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/isom.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,209 @@ +/* + * ISO Media common code + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2002 Francois Revol + * copyright (c) 2006 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_ISOM_H +#define AVFORMAT_ISOM_H + +#include "avio.h" +#include "internal.h" +#include "dv.h" + +/* isom.c */ +extern const AVCodecTag ff_mp4_obj_type[]; +extern const AVCodecTag ff_codec_movvideo_tags[]; +extern const AVCodecTag ff_codec_movaudio_tags[]; +extern const AVCodecTag ff_codec_movsubtitle_tags[]; + +int ff_mov_iso639_to_lang(const char lang[4], int mp4); +int ff_mov_lang_to_iso639(unsigned code, char to[4]); + +/* the QuickTime file format is quite convoluted... + * it has lots of index tables, each indexing something in another one... + * Here we just use what is needed to read the chunks + */ + +typedef struct MOVStts { + int count; + int duration; +} MOVStts; + +typedef struct MOVStsc { + int first; + int count; + int id; +} MOVStsc; + +typedef struct MOVDref { + uint32_t type; + char *path; + char *dir; + char volume[28]; + char filename[64]; + int16_t nlvl_to, nlvl_from; +} MOVDref; + +typedef struct MOVAtom { + uint32_t type; + int64_t size; /* total size (excluding the size and type fields) */ +} MOVAtom; + +struct MOVParseTableEntry; + +typedef struct MOVFragment { + unsigned track_id; + uint64_t base_data_offset; + uint64_t moof_offset; + unsigned stsd_id; + unsigned duration; + unsigned size; + unsigned flags; +} MOVFragment; + +typedef struct MOVTrackExt { + unsigned track_id; + unsigned stsd_id; + unsigned duration; + unsigned size; + unsigned flags; +} MOVTrackExt; + +typedef struct MOVSbgp { + unsigned int count; + unsigned int index; +} MOVSbgp; + +typedef struct MOVStreamContext { + AVIOContext *pb; + int pb_is_copied; + int ffindex; ///< AVStream index + int next_chunk; + unsigned int chunk_count; + int64_t *chunk_offsets; + unsigned int stts_count; + MOVStts *stts_data; + unsigned int ctts_count; + MOVStts *ctts_data; + unsigned int stsc_count; + MOVStsc *stsc_data; + unsigned int stps_count; + unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop + int ctts_index; + int ctts_sample; + unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom + unsigned int alt_sample_size; ///< always contains sample size from stsz atom + unsigned int sample_count; + int *sample_sizes; + int keyframe_absent; + unsigned int keyframe_count; + int *keyframes; + int time_scale; + int64_t empty_duration; ///< empty duration of the first edit list entry + int64_t start_time; ///< start time of the media + int64_t time_offset; ///< time offset of the edit list entries + int current_sample; + unsigned int bytes_per_frame; + unsigned int samples_per_frame; + int dv_audio_container; + int pseudo_stream_id; ///< -1 means demux all ids + int16_t audio_cid; ///< stsd audio compression id + unsigned drefs_count; + MOVDref *drefs; + int dref_id; + int timecode_track; + int wrong_dts; ///< dts are wrong due to huge ctts offset (iMovie files) + int width; ///< tkhd width + int height; ///< tkhd height + int dts_shift; ///< dts shift when ctts is negative + uint32_t palette[256]; + int has_palette; + int64_t data_size; + uint32_t tmcd_flags; ///< tmcd track flags + int64_t track_end; ///< used for dts generation in fragmented movie files + int start_pad; ///< amount of samples to skip due to enc-dec delay + unsigned int rap_group_count; + MOVSbgp *rap_group; +} MOVStreamContext; + +typedef struct MOVContext { + AVClass *avclass; + AVFormatContext *fc; + int time_scale; + int64_t duration; ///< duration of the longest track + int found_moov; ///< 'moov' atom has been found + int found_mdat; ///< 'mdat' atom has been found + DVDemuxContext *dv_demux; + AVFormatContext *dv_fctx; + int isom; ///< 1 if file is ISO Media (mp4/3gp) + MOVFragment fragment; ///< current fragment in moof atom + MOVTrackExt *trex_data; + unsigned trex_count; + int itunes_metadata; ///< metadata are itunes style + int chapter_track; + int use_absolute_path; + int ignore_editlist; + int64_t next_root_atom; ///< offset of the next root atom +} MOVContext; + +int ff_mp4_read_descr_len(AVIOContext *pb); +int ff_mp4_read_descr(AVFormatContext *fc, AVIOContext *pb, int *tag); +int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext *pb); +void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id); + +#define MP4ODescrTag 0x01 +#define MP4IODescrTag 0x02 +#define MP4ESDescrTag 0x03 +#define MP4DecConfigDescrTag 0x04 +#define MP4DecSpecificDescrTag 0x05 +#define MP4SLDescrTag 0x06 + +#define MOV_TFHD_BASE_DATA_OFFSET 0x01 +#define MOV_TFHD_STSD_ID 0x02 +#define MOV_TFHD_DEFAULT_DURATION 0x08 +#define MOV_TFHD_DEFAULT_SIZE 0x10 +#define MOV_TFHD_DEFAULT_FLAGS 0x20 +#define MOV_TFHD_DURATION_IS_EMPTY 0x010000 + +#define MOV_TRUN_DATA_OFFSET 0x01 +#define MOV_TRUN_FIRST_SAMPLE_FLAGS 0x04 +#define MOV_TRUN_SAMPLE_DURATION 0x100 +#define MOV_TRUN_SAMPLE_SIZE 0x200 +#define MOV_TRUN_SAMPLE_FLAGS 0x400 +#define MOV_TRUN_SAMPLE_CTS 0x800 + +#define MOV_FRAG_SAMPLE_FLAG_DEGRADATION_PRIORITY_MASK 0x0000ffff +#define MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC 0x00010000 +#define MOV_FRAG_SAMPLE_FLAG_PADDING_MASK 0x000e0000 +#define MOV_FRAG_SAMPLE_FLAG_REDUNDANCY_MASK 0x00300000 +#define MOV_FRAG_SAMPLE_FLAG_DEPENDED_MASK 0x00c00000 +#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_MASK 0x03000000 + +#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_NO 0x02000000 +#define MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES 0x01000000 + +int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom); +enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags); + +int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries); +void ff_mov_write_chan(AVIOContext *pb, int64_t channel_layout); + +#endif /* AVFORMAT_ISOM_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/isom.o Binary file ffmpeg/libavformat/isom.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iss.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iss.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,144 @@ +/* + * ISS (.iss) file demuxer + * Copyright (c) 2008 Jaikrishnan Menon + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Funcom ISS file demuxer + * @author Jaikrishnan Menon + * @see http://wiki.multimedia.cx/index.php?title=FunCom_ISS + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" +#include "libavutil/avstring.h" + +#define ISS_SIG "IMA_ADPCM_Sound" +#define ISS_SIG_LEN 15 +#define MAX_TOKEN_SIZE 20 + +typedef struct { + int packet_size; + int sample_start_pos; +} IssDemuxContext; + +static void get_token(AVIOContext *s, char *buf, int maxlen) +{ + int i = 0; + char c; + + while ((c = avio_r8(s))) { + if(c == ' ') + break; + if (i < maxlen-1) + buf[i++] = c; + } + + if(!c) + avio_r8(s); + + buf[i] = 0; /* Ensure null terminated, but may be truncated */ +} + +static int iss_probe(AVProbeData *p) +{ + if (strncmp(p->buf, ISS_SIG, ISS_SIG_LEN)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static av_cold int iss_read_header(AVFormatContext *s) +{ + IssDemuxContext *iss = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + char token[MAX_TOKEN_SIZE]; + int stereo, rate_divisor; + + get_token(pb, token, sizeof(token)); //"IMA_ADPCM_Sound" + get_token(pb, token, sizeof(token)); //packet size + sscanf(token, "%d", &iss->packet_size); + get_token(pb, token, sizeof(token)); //File ID + get_token(pb, token, sizeof(token)); //out size + get_token(pb, token, sizeof(token)); //stereo + sscanf(token, "%d", &stereo); + get_token(pb, token, sizeof(token)); //Unknown1 + get_token(pb, token, sizeof(token)); //RateDivisor + sscanf(token, "%d", &rate_divisor); + get_token(pb, token, sizeof(token)); //Unknown2 + get_token(pb, token, sizeof(token)); //Version ID + get_token(pb, token, sizeof(token)); //Size + + if (iss->packet_size <= 0) { + av_log(s, AV_LOG_ERROR, "packet_size %d is invalid\n", iss->packet_size); + return AVERROR_INVALIDDATA; + } + + iss->sample_start_pos = avio_tell(pb); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_ISS; + if (stereo) { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + st->codec->sample_rate = 44100; + if(rate_divisor > 0) + st->codec->sample_rate /= rate_divisor; + st->codec->bits_per_coded_sample = 4; + st->codec->bit_rate = st->codec->channels * st->codec->sample_rate + * st->codec->bits_per_coded_sample; + st->codec->block_align = iss->packet_size; + avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); + + return 0; +} + +static int iss_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + IssDemuxContext *iss = s->priv_data; + int ret = av_get_packet(s->pb, pkt, iss->packet_size); + + if(ret != iss->packet_size) + return AVERROR(EIO); + + pkt->stream_index = 0; + pkt->pts = avio_tell(s->pb) - iss->sample_start_pos; + if(s->streams[0]->codec->channels > 0) + pkt->pts /= s->streams[0]->codec->channels*2; + return 0; +} + +AVInputFormat ff_iss_demuxer = { + .name = "iss", + .long_name = NULL_IF_CONFIG_SMALL("Funcom ISS"), + .priv_data_size = sizeof(IssDemuxContext), + .read_probe = iss_probe, + .read_header = iss_read_header, + .read_packet = iss_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iss.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iss.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/iss.o libavformat/iss.o: libavformat/iss.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavutil/avstring.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iss.o Binary file ffmpeg/libavformat/iss.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iv8.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iv8.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + + +static int probe(AVProbeData *p) +{ + // the single file I have starts with that, I do not know if others do, too + if( p->buf[0] == 1 + && p->buf[1] == 1 + && p->buf[2] == 3 + && p->buf[3] == 0xB8 + && p->buf[4] == 0x80 + && p->buf[5] == 0x60 + ) + return AVPROBE_SCORE_MAX-2; + + return 0; +} + +static int read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG4; + st->need_parsing = AVSTREAM_PARSE_FULL; + avpriv_set_pts_info(st, 64, 1, 90000); + + return 0; + +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size, pts, type, flags; + int first_pkt = 0; + int frame_complete = 0; + + while (!frame_complete) { + + type = avio_rb16(s->pb); // 257 or 258 + size = avio_rb16(s->pb); + flags = avio_rb16(s->pb); //some flags, 0x80 indicates end of frame + avio_rb16(s->pb); //packet number + pts = avio_rb32(s->pb); + avio_rb32(s->pb); //6A 13 E3 88 + + frame_complete = flags & 0x80; + + size -= 12; + if (size < 1) + return -1; + + if (type == 258) { + avio_skip(s->pb, size); + frame_complete = 0; + continue; + } + + if (!first_pkt) { + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + first_pkt = 1; + pkt->pts = pts; + pkt->pos -= 16; + } else { + ret = av_append_packet(s->pb, pkt, size); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "failed to grow packet\n"); + av_free_packet(pkt); + return ret; + } + } + if (ret < size) { + av_log(s, AV_LOG_ERROR, "Truncated packet! Read %d of %d bytes\n", + ret, size); + pkt->flags |= AV_PKT_FLAG_CORRUPT; + break; + } + } + pkt->stream_index = 0; + + return 0; +} + +AVInputFormat ff_iv8_demuxer = { + .name = "iv8", + .long_name = NULL_IF_CONFIG_SMALL("IndigoVision 8000 video"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iv8.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/iv8.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/iv8.o libavformat/iv8.o: libavformat/iv8.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/iv8.o Binary file ffmpeg/libavformat/iv8.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ivfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include "libavutil/intreadwrite.h" + +static int probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('D','K','I','F') + && !AV_RL16(p->buf+4) && AV_RL16(p->buf+6) == 32) + return AVPROBE_SCORE_MAX-2; + + return 0; +} + +static int read_header(AVFormatContext *s) +{ + AVStream *st; + AVRational time_base; + + avio_rl32(s->pb); // DKIF + avio_rl16(s->pb); // version + avio_rl16(s->pb); // header size + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = avio_rl32(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codec->codec_tag); + st->codec->width = avio_rl16(s->pb); + st->codec->height = avio_rl16(s->pb); + time_base.den = avio_rl32(s->pb); + time_base.num = avio_rl32(s->pb); + st->duration = avio_rl64(s->pb); + + st->need_parsing = AVSTREAM_PARSE_HEADERS; + + if (!time_base.den || !time_base.num) { + av_log(s, AV_LOG_ERROR, "Invalid frame rate\n"); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(st, 64, time_base.num, time_base.den); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size = avio_rl32(s->pb); + int64_t pts = avio_rl64(s->pb); + + ret = av_get_packet(s->pb, pkt, size); + pkt->stream_index = 0; + pkt->pts = pts; + pkt->pos -= 12; + + return ret; +} + +AVInputFormat ff_ivf_demuxer = { + .name = "ivf", + .long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, + .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ivfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/ivfdec.o libavformat/ivfdec.o: libavformat/ivfdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/riff.h libavformat/metadata.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfdec.o Binary file ffmpeg/libavformat/ivfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ivfenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "libavutil/intreadwrite.h" + +static int ivf_write_header(AVFormatContext *s) +{ + AVCodecContext *ctx; + AVIOContext *pb = s->pb; + + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "Format supports only exactly one video stream\n"); + return AVERROR(EINVAL); + } + ctx = s->streams[0]->codec; + if (ctx->codec_type != AVMEDIA_TYPE_VIDEO || ctx->codec_id != AV_CODEC_ID_VP8) { + av_log(s, AV_LOG_ERROR, "Currently only VP8 is supported!\n"); + return AVERROR(EINVAL); + } + avio_write(pb, "DKIF", 4); + avio_wl16(pb, 0); // version + avio_wl16(pb, 32); // header length + avio_wl32(pb, ctx->codec_tag ? ctx->codec_tag : AV_RL32("VP80")); + avio_wl16(pb, ctx->width); + avio_wl16(pb, ctx->height); + avio_wl32(pb, s->streams[0]->time_base.den); + avio_wl32(pb, s->streams[0]->time_base.num); + avio_wl64(pb, s->streams[0]->duration); // TODO: duration or number of frames?!? + + return 0; +} + +static int ivf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + avio_wl32(pb, pkt->size); + avio_wl64(pb, pkt->pts); + avio_write(pb, pkt->data, pkt->size); + + return 0; +} + +AVOutputFormat ff_ivf_muxer = { + .name = "ivf", + .long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), + .extensions = "ivf", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_VP8, + .write_header = ivf_write_header, + .write_packet = ivf_write_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ivfenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/ivfenc.o libavformat/ivfenc.o: libavformat/ivfenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ivfenc.o Binary file ffmpeg/libavformat/ivfenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jacosubdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * JACOsub subtitle demuxer + * @see http://unicorn.us.com/jacosub/jscripts.html + * @todo Support P[ALETTE] directive. + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavcodec/internal.h" +#include "libavcodec/jacosub.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + int shift; + unsigned timeres; + FFDemuxSubtitlesQueue q; +} JACOsubContext; + +static int timed_line(const char *ptr) +{ + char c; + return (sscanf(ptr, "%*u:%*u:%*u.%*u %*u:%*u:%*u.%*u %c", &c) == 1 || + sscanf(ptr, "@%*u @%*u %c", &c) == 1); +} + +static int jacosub_probe(AVProbeData *p) +{ + const char *ptr = p->buf; + const char *ptr_end = p->buf + p->buf_size; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + + while (ptr < ptr_end) { + while (jss_whitespace(*ptr)) + ptr++; + if (*ptr != '#' && *ptr != '\n') { + if (timed_line(ptr)) + return AVPROBE_SCORE_MAX/2 + 1; + return 0; + } + ptr += strcspn(ptr, "\n") + 1; + } + return 0; +} + +static const char * const cmds[] = { + "CLOCKPAUSE", + "DIRECTIVE", + "FONT", + "HRES", + "INCLUDE", + "PALETTE", + "QUANTIZE", + "RAMP", + "SHIFT", + "TIMERES", +}; + +static int get_jss_cmd(char k) +{ + int i; + + k = av_toupper(k); + for (i = 0; i < FF_ARRAY_ELEMS(cmds); i++) + if (k == cmds[i][0]) + return i; + return -1; +} + +static int jacosub_read_close(AVFormatContext *s) +{ + JACOsubContext *jacosub = s->priv_data; + ff_subtitles_queue_clean(&jacosub->q); + return 0; +} + +static const char *read_ts(JACOsubContext *jacosub, const char *buf, + int64_t *start, int *duration) +{ + int len; + unsigned hs, ms, ss, fs; // hours, minutes, seconds, frame start + unsigned he, me, se, fe; // hours, minutes, seconds, frame end + int ts_start, ts_end; + + /* timed format */ + if (sscanf(buf, "%u:%u:%u.%u %u:%u:%u.%u %n", + &hs, &ms, &ss, &fs, + &he, &me, &se, &fe, &len) == 8) { + ts_start = (hs*3600 + ms*60 + ss) * jacosub->timeres + fs; + ts_end = (he*3600 + me*60 + se) * jacosub->timeres + fe; + goto shift_and_ret; + } + + /* timestamps format */ + if (sscanf(buf, "@%u @%u %n", &ts_start, &ts_end, &len) == 2) + goto shift_and_ret; + + return NULL; + +shift_and_ret: + ts_start = (ts_start + jacosub->shift) * 100 / jacosub->timeres; + ts_end = (ts_end + jacosub->shift) * 100 / jacosub->timeres; + *start = ts_start; + *duration = ts_start + ts_end; + return buf + len; +} + +static int get_shift(int timeres, const char *buf) +{ + int sign = 1; + int a = 0, b = 0, c = 0, d = 0; +#define SSEP "%*1[.:]" + int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &a, &b, &c, &d); +#undef SSEP + + if (*buf == '-' || a < 0) { + sign = -1; + a = FFABS(a); + } + + switch (n) { + case 4: return sign * ((a*3600 + b*60 + c) * timeres + d); + case 3: return sign * (( a*60 + b) * timeres + c); + case 2: return sign * (( a) * timeres + b); + } + + return 0; +} + +static int jacosub_read_header(AVFormatContext *s) +{ + AVBPrint header; + AVIOContext *pb = s->pb; + char line[JSS_MAX_LINESIZE]; + JACOsubContext *jacosub = s->priv_data; + int shift_set = 0; // only the first shift matters + int merge_line = 0; + int i, ret; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_JACOSUB; + + jacosub->timeres = 30; + + av_bprint_init(&header, 1024+FF_INPUT_BUFFER_PADDING_SIZE, 4096); + + while (!url_feof(pb)) { + int cmd_len; + const char *p = line; + int64_t pos = avio_tell(pb); + int len = ff_get_line(pb, line, sizeof(line)); + + p = jss_skip_whitespace(p); + + /* queue timed line */ + if (merge_line || timed_line(p)) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); + continue; + } + + /* skip all non-compiler commands and focus on the command */ + if (*p != '#') + continue; + p++; + i = get_jss_cmd(p[0]); + if (i == -1) + continue; + + /* trim command + spaces */ + cmd_len = strlen(cmds[i]); + if (av_strncasecmp(p, cmds[i], cmd_len) == 0) + p += cmd_len; + else + p++; + p = jss_skip_whitespace(p); + + /* handle commands which affect the whole script */ + switch (cmds[i][0]) { + case 'S': // SHIFT command affect the whole script... + if (!shift_set) { + jacosub->shift = get_shift(jacosub->timeres, p); + shift_set = 1; + } + av_bprintf(&header, "#S %s", p); + break; + case 'T': // ...but must be placed after TIMERES + jacosub->timeres = strtol(p, NULL, 10); + if (!jacosub->timeres) + jacosub->timeres = 30; + else + av_bprintf(&header, "#T %s", p); + break; + } + } + + /* general/essential directives in the extradata */ + ret = avpriv_bprint_to_extradata(st->codec, &header); + if (ret < 0) + return ret; + + /* SHIFT and TIMERES affect the whole script so packet timing can only be + * done in a second pass */ + for (i = 0; i < jacosub->q.nb_subs; i++) { + AVPacket *sub = &jacosub->q.subs[i]; + read_ts(jacosub, sub->data, &sub->pts, &sub->duration); + } + ff_subtitles_queue_finalize(&jacosub->q); + + return 0; +} + +static int jacosub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + JACOsubContext *jacosub = s->priv_data; + return ff_subtitles_queue_read_packet(&jacosub->q, pkt); +} + +static int jacosub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + JACOsubContext *jacosub = s->priv_data; + return ff_subtitles_queue_seek(&jacosub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +AVInputFormat ff_jacosub_demuxer = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .priv_data_size = sizeof(JACOsubContext), + .read_probe = jacosub_probe, + .read_header = jacosub_read_header, + .read_packet = jacosub_read_packet, + .read_seek2 = jacosub_read_seek, + .read_close = jacosub_read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jacosubdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/jacosubdec.o libavformat/jacosubdec.o: \ + libavformat/jacosubdec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h \ + libavcodec/internal.h libavutil/mathematics.h libavcodec/avcodec.h \ + config.h libavcodec/jacosub.h libavutil/avstring.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubdec.o Binary file ffmpeg/libavformat/jacosubdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jacosubenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,42 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + +static int jacosub_write_header(AVFormatContext *s) +{ + const AVCodecContext *avctx = s->streams[0]->codec; + + if (avctx->extradata_size) { + avio_write(s->pb, avctx->extradata, avctx->extradata_size - 1); + avio_flush(s->pb); + } + return 0; +} + +AVOutputFormat ff_jacosub_muxer = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), + .mime_type = "text/x-jacosub", + .extensions = "jss,js", + .write_header = jacosub_write_header, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_TS_NONSTRICT, + .subtitle_codec = AV_CODEC_ID_JACOSUB, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jacosubenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/jacosubenc.o libavformat/jacosubenc.o: \ + libavformat/jacosubenc.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jacosubenc.o Binary file ffmpeg/libavformat/jacosubenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jvdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jvdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,239 @@ +/* + * Bitmap Brothers JV demuxer + * Copyright (c) 2005, 2011 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Bitmap Brothers JV demuxer + * @author Peter Ross + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define JV_PREAMBLE_SIZE 5 + +typedef struct { + int audio_size; /** audio packet size (bytes) */ + int video_size; /** video packet size (bytes) */ + int palette_size; /** palette size (bytes) */ + int video_type; /** per-frame video compression type */ +} JVFrame; + +typedef struct { + JVFrame *frames; + enum { + JV_AUDIO = 0, + JV_VIDEO, + JV_PADDING + } state; + int64_t pts; +} JVDemuxContext; + +#define MAGIC " Compression by John M Phillips Copyright (C) 1995 The Bitmap Brothers Ltd." + +static int read_probe(AVProbeData *pd) +{ + if (pd->buf[0] == 'J' && pd->buf[1] == 'V' && strlen(MAGIC) <= pd->buf_size - 4 && + !memcmp(pd->buf + 4, MAGIC, strlen(MAGIC))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int read_header(AVFormatContext *s) +{ + JVDemuxContext *jv = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *vst, *ast; + int64_t audio_pts = 0; + int64_t offset; + int i; + + avio_skip(pb, 80); + + ast = avformat_new_stream(s, NULL); + vst = avformat_new_stream(s, NULL); + if (!ast || !vst) + return AVERROR(ENOMEM); + + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_JV; + vst->codec->codec_tag = 0; /* no fourcc */ + vst->codec->width = avio_rl16(pb); + vst->codec->height = avio_rl16(pb); + vst->duration = + vst->nb_frames = + ast->nb_index_entries = avio_rl16(pb); + avpriv_set_pts_info(vst, 64, avio_rl16(pb), 1000); + + avio_skip(pb, 4); + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_PCM_U8; + ast->codec->codec_tag = 0; /* no fourcc */ + ast->codec->sample_rate = avio_rl16(pb); + ast->codec->channels = 1; + ast->codec->channel_layout = AV_CH_LAYOUT_MONO; + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + + avio_skip(pb, 10); + + ast->index_entries = av_malloc(ast->nb_index_entries * sizeof(*ast->index_entries)); + if (!ast->index_entries) + return AVERROR(ENOMEM); + + jv->frames = av_malloc(ast->nb_index_entries * sizeof(JVFrame)); + if (!jv->frames) + return AVERROR(ENOMEM); + + offset = 0x68 + ast->nb_index_entries * 16; + for(i = 0; i < ast->nb_index_entries; i++) { + AVIndexEntry *e = ast->index_entries + i; + JVFrame *jvf = jv->frames + i; + + /* total frame size including audio, video, palette data and padding */ + e->size = avio_rl32(pb); + e->timestamp = i; + e->pos = offset; + offset += e->size; + + jvf->audio_size = avio_rl32(pb); + jvf->video_size = avio_rl32(pb); + jvf->palette_size = avio_r8(pb) ? 768 : 0; + jvf->video_size = FFMIN(FFMAX(jvf->video_size, 0), + INT_MAX - JV_PREAMBLE_SIZE - jvf->palette_size); + if (avio_r8(pb)) + av_log(s, AV_LOG_WARNING, "unsupported audio codec\n"); + jvf->video_type = avio_r8(pb); + avio_skip(pb, 1); + + e->timestamp = jvf->audio_size ? audio_pts : AV_NOPTS_VALUE; + audio_pts += jvf->audio_size; + + e->flags = jvf->video_type != 1 ? AVINDEX_KEYFRAME : 0; + } + + jv->state = JV_AUDIO; + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + JVDemuxContext *jv = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast = s->streams[0]; + + while (!url_feof(s->pb) && jv->pts < ast->nb_index_entries) { + const AVIndexEntry *e = ast->index_entries + jv->pts; + const JVFrame *jvf = jv->frames + jv->pts; + + switch(jv->state) { + case JV_AUDIO: + jv->state++; + if (jvf->audio_size ) { + if (av_get_packet(s->pb, pkt, jvf->audio_size) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 0; + pkt->pts = e->timestamp; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; + } + case JV_VIDEO: + jv->state++; + if (jvf->video_size || jvf->palette_size) { + int size = jvf->video_size + jvf->palette_size; + if (av_new_packet(pkt, size + JV_PREAMBLE_SIZE)) + return AVERROR(ENOMEM); + + AV_WL32(pkt->data, jvf->video_size); + pkt->data[4] = jvf->video_type; + if ((size = avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size)) < 0) + return AVERROR(EIO); + + pkt->size = size + JV_PREAMBLE_SIZE; + pkt->stream_index = 1; + pkt->pts = jv->pts; + if (jvf->video_type != 1) + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; + } + case JV_PADDING: + avio_skip(pb, FFMAX(e->size - jvf->audio_size - jvf->video_size + - jvf->palette_size, 0)); + jv->state = JV_AUDIO; + jv->pts++; + } + } + + return AVERROR(EIO); +} + +static int read_seek(AVFormatContext *s, int stream_index, + int64_t ts, int flags) +{ + JVDemuxContext *jv = s->priv_data; + AVStream *ast = s->streams[0]; + int i; + + if (flags & (AVSEEK_FLAG_BYTE|AVSEEK_FLAG_FRAME)) + return AVERROR(ENOSYS); + + switch(stream_index) { + case 0: + i = av_index_search_timestamp(ast, ts, flags); + break; + case 1: + i = ts; + break; + default: + return 0; + } + + if (i < 0 || i >= ast->nb_index_entries) + return 0; + if (avio_seek(s->pb, ast->index_entries[i].pos, SEEK_SET) < 0) + return -1; + + jv->state = JV_AUDIO; + jv->pts = i; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + JVDemuxContext *jv = s->priv_data; + + av_freep(&jv->frames); + + return 0; +} + +AVInputFormat ff_jv_demuxer = { + .name = "jv", + .long_name = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"), + .priv_data_size = sizeof(JVDemuxContext), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .read_seek = read_seek, + .read_close = read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jvdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/jvdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/jvdec.o libavformat/jvdec.o: libavformat/jvdec.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/jvdec.o Binary file ffmpeg/libavformat/jvdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/latmenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/latmenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,230 @@ +/* + * LATM/LOAS muxer + * Copyright (c) 2011 Kieran Kunhya + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/mpeg4audio.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "rawenc.h" + +#define MAX_EXTRADATA_SIZE 1024 + +typedef struct { + AVClass *av_class; + int off; + int channel_conf; + int object_type; + int counter; + int mod; + uint8_t buffer[0x1fff + MAX_EXTRADATA_SIZE + 1024]; +} LATMContext; + +static const AVOption options[] = { + {"smc-interval", "StreamMuxConfig interval.", + offsetof(LATMContext, mod), AV_OPT_TYPE_INT, {.i64 = 0x0014}, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, + {NULL}, +}; + +static const AVClass latm_muxer_class = { + .class_name = "LATM/LOAS muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int latm_decode_extradata(LATMContext *ctx, uint8_t *buf, int size) +{ + MPEG4AudioConfig m4ac; + + if (size > MAX_EXTRADATA_SIZE) { + av_log(ctx, AV_LOG_ERROR, "Extradata is larger than currently supported.\n"); + return AVERROR_INVALIDDATA; + } + ctx->off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1); + if (ctx->off < 0) + return ctx->off; + + if (ctx->object_type == AOT_ALS && (ctx->off & 7)) { + // as long as avpriv_mpeg4audio_get_config works correctly this is impossible + av_log(ctx, AV_LOG_ERROR, "BUG: ALS offset is not byte-aligned\n"); + return AVERROR_INVALIDDATA; + } + /* FIXME: are any formats not allowed in LATM? */ + + if (m4ac.object_type > AOT_SBR && m4ac.object_type != AOT_ALS) { + av_log(ctx, AV_LOG_ERROR, "Muxing MPEG-4 AOT %d in LATM is not supported\n", m4ac.object_type); + return AVERROR_INVALIDDATA; + } + ctx->channel_conf = m4ac.chan_config; + ctx->object_type = m4ac.object_type; + + return 0; +} + +static int latm_write_header(AVFormatContext *s) +{ + LATMContext *ctx = s->priv_data; + AVCodecContext *avctx = s->streams[0]->codec; + + if (avctx->codec_id == AV_CODEC_ID_AAC_LATM) + return 0; + + if (avctx->extradata_size > 0 && + latm_decode_extradata(ctx, avctx->extradata, avctx->extradata_size) < 0) + return AVERROR_INVALIDDATA; + + return 0; +} + +static void latm_write_frame_header(AVFormatContext *s, PutBitContext *bs) +{ + LATMContext *ctx = s->priv_data; + AVCodecContext *avctx = s->streams[0]->codec; + int header_size; + + /* AudioMuxElement */ + put_bits(bs, 1, !!ctx->counter); + + if (!ctx->counter) { + /* StreamMuxConfig */ + put_bits(bs, 1, 0); /* audioMuxVersion */ + put_bits(bs, 1, 1); /* allStreamsSameTimeFraming */ + put_bits(bs, 6, 0); /* numSubFrames */ + put_bits(bs, 4, 0); /* numProgram */ + put_bits(bs, 3, 0); /* numLayer */ + + /* AudioSpecificConfig */ + if (ctx->object_type == AOT_ALS) { + header_size = avctx->extradata_size-(ctx->off >> 3); + avpriv_copy_bits(bs, &avctx->extradata[ctx->off >> 3], header_size); + } else { + // + 3 assumes not scalable and dependsOnCoreCoder == 0, + // see decode_ga_specific_config in libavcodec/aacdec.c + avpriv_copy_bits(bs, avctx->extradata, ctx->off + 3); + + if (!ctx->channel_conf) { + GetBitContext gb; + init_get_bits(&gb, avctx->extradata, avctx->extradata_size * 8); + skip_bits_long(&gb, ctx->off + 3); + avpriv_copy_pce_data(bs, &gb); + } + } + + put_bits(bs, 3, 0); /* frameLengthType */ + put_bits(bs, 8, 0xff); /* latmBufferFullness */ + + put_bits(bs, 1, 0); /* otherDataPresent */ + put_bits(bs, 1, 0); /* crcCheckPresent */ + } + + ctx->counter++; + ctx->counter %= ctx->mod; +} + +static int latm_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + LATMContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + PutBitContext bs; + int i, len; + uint8_t loas_header[] = "\x56\xe0\x00"; + + if (s->streams[0]->codec->codec_id == AV_CODEC_ID_AAC_LATM) + return ff_raw_write_packet(s, pkt); + + if (pkt->size > 2 && pkt->data[0] == 0xff && (pkt->data[1] >> 4) == 0xf) { + av_log(s, AV_LOG_ERROR, "ADTS header detected - ADTS will not be incorrectly muxed into LATM\n"); + return AVERROR_INVALIDDATA; + } + + if (!s->streams[0]->codec->extradata) { + if(pkt->size > 2 && pkt->data[0] == 0x56 && (pkt->data[1] >> 4) == 0xe && + (AV_RB16(pkt->data + 1) & 0x1FFF) + 3 == pkt->size) + return ff_raw_write_packet(s, pkt); + else + return AVERROR_INVALIDDATA; + } + + if (pkt->size > 0x1fff) + goto too_large; + + init_put_bits(&bs, ctx->buffer, pkt->size+1024+MAX_EXTRADATA_SIZE); + + latm_write_frame_header(s, &bs); + + /* PayloadLengthInfo() */ + for (i = 0; i <= pkt->size-255; i+=255) + put_bits(&bs, 8, 255); + + put_bits(&bs, 8, pkt->size-i); + + /* The LATM payload is written unaligned */ + + /* PayloadMux() */ + if (pkt->size && (pkt->data[0] & 0xe1) == 0x81) { + // Convert byte-aligned DSE to non-aligned. + // Due to the input format encoding we know that + // it is naturally byte-aligned in the input stream, + // so there are no padding bits to account for. + // To avoid having to add padding bits and rearrange + // the whole stream we just remove the byte-align flag. + // This allows us to remux our FATE AAC samples into latm + // files that are still playable with minimal effort. + put_bits(&bs, 8, pkt->data[0] & 0xfe); + avpriv_copy_bits(&bs, pkt->data + 1, 8*pkt->size - 8); + } else + avpriv_copy_bits(&bs, pkt->data, 8*pkt->size); + + avpriv_align_put_bits(&bs); + flush_put_bits(&bs); + + len = put_bits_count(&bs) >> 3; + + if (len > 0x1fff) + goto too_large; + + loas_header[1] |= (len >> 8) & 0x1f; + loas_header[2] |= len & 0xff; + + avio_write(pb, loas_header, 3); + avio_write(pb, ctx->buffer, len); + + return 0; + +too_large: + av_log(s, AV_LOG_ERROR, "LATM packet size larger than maximum size 0x1fff\n"); + return AVERROR_INVALIDDATA; +} + +AVOutputFormat ff_latm_muxer = { + .name = "latm", + .long_name = NULL_IF_CONFIG_SMALL("LOAS/LATM"), + .mime_type = "audio/MP4A-LATM", + .extensions = "latm,loas", + .priv_data_size = sizeof(LATMContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_NONE, + .write_header = latm_write_header, + .write_packet = latm_write_packet, + .priv_class = &latm_muxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/latmenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/latmenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/latmenc.o libavformat/latmenc.o: libavformat/latmenc.c \ + libavcodec/get_bits.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/put_bits.h libavutil/bswap.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavcodec/mpeg4audio.h \ + libavcodec/get_bits.h libavcodec/put_bits.h libavutil/opt.h \ + libavformat/avformat.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/latmenc.o Binary file ffmpeg/libavformat/latmenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libavformat.a Binary file ffmpeg/libavformat/libavformat.a has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libavformat.pc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/libavformat.pc Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,14 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: libavformat +Description: FFmpeg container format library +Version: 55.3.100 +Requires: libavcodec = 55.5.100 +Requires.private: +Conflicts: +Libs: -L${libdir} -lavformat -liconv -lm -lbz2 -lz +Libs.private: +Cflags: -I${includedir} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libavformat.v --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/libavformat.v Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,37 @@ +LIBAVFORMAT_$MAJOR { + global: av*; + #FIXME those are for ffserver + ff_inet_aton; + ff_socket_nonblock; + ffm_set_write_index; + ffm_read_write_index; + ffm_write_write_index; + ff_mpegts_parse_close; + ff_mpegts_parse_open; + ff_mpegts_parse_packet; + ff_rtsp_parse_line; + ff_rtp_get_local_rtp_port; + ff_rtp_get_local_rtcp_port; + ffio_open_dyn_packet_buf; + ffio_set_buf_size; + ffurl_close; + ffurl_open; + ffurl_read_complete; + ffurl_seek; + ffurl_size; + ffurl_write; + ffurl_protocol_next; + url_open; + url_close; + url_write; + #those are deprecated, remove on next bump + url_*; + ff_timefilter_destroy; + ff_timefilter_new; + ff_timefilter_update; + ff_timefilter_reset; + get_*; + put_*; + ff_codec_get_id; + local: *; +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libmodplug.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/libmodplug.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,368 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** +* @file +* ModPlug demuxer +* @todo better probing than extensions matching +*/ + +#include +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "internal.h" + +typedef struct ModPlugContext { + const AVClass *class; + ModPlugFile *f; + uint8_t *buf; ///< input file content + + /* options */ + int noise_reduction; + int reverb_depth; + int reverb_delay; + int bass_amount; + int bass_range; + int surround_depth; + int surround_delay; + + int max_size; ///< max file size to allocate + + /* optional video stream */ + double ts_per_packet; ///< used to define the pts/dts using packet_count; + int packet_count; ///< total number of audio packets + int print_textinfo; ///< bool flag for printing speed, tempo, order, ... + int video_stream; ///< 1 if the user want a video stream, otherwise 0 + int w; ///< video stream width in char (one char = 8x8px) + int h; ///< video stream height in char (one char = 8x8px) + int video_switch; ///< 1 if current packet is video, otherwise 0 + int fsize; ///< constant frame size + int linesize; ///< line size in bytes + char *color_eval; ///< color eval user input expression + AVExpr *expr; ///< parsed color eval expression +} ModPlugContext; + +static const char *var_names[] = { + "x", "y", + "w", "h", + "t", + "speed", "tempo", "order", "pattern", "row", + NULL +}; + +enum var_name { + VAR_X, VAR_Y, + VAR_W, VAR_H, + VAR_TIME, + VAR_SPEED, VAR_TEMPO, VAR_ORDER, VAR_PATTERN, VAR_ROW, + VAR_VARS_NB +}; + +#define FF_MODPLUG_MAX_FILE_SIZE (100 * 1<<20) // 100M +#define FF_MODPLUG_DEF_FILE_SIZE ( 5 * 1<<20) // 5M + +#define OFFSET(x) offsetof(ModPlugContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"noise_reduction", "Enable noise reduction 0(off)-1(on)", OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"reverb_depth", "Reverb level 0(quiet)-100(loud)", OFFSET(reverb_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"reverb_delay", "Reverb delay in ms, usually 40-200ms", OFFSET(reverb_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"bass_amount", "XBass level 0(quiet)-100(loud)", OFFSET(bass_amount), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"bass_range", "XBass cutoff in Hz 10-100", OFFSET(bass_range), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_depth", "Surround level 0(quiet)-100(heavy)", OFFSET(surround_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, + {"surround_delay", "Surround delay in ms, usually 5-40ms", OFFSET(surround_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, + {"max_size", "Max file size supported (in bytes). Default is 5MB. Set to 0 for no limit (not recommended)", + OFFSET(max_size), AV_OPT_TYPE_INT, {.i64 = FF_MODPLUG_DEF_FILE_SIZE}, 0, FF_MODPLUG_MAX_FILE_SIZE, D}, + {"video_stream_expr", "Color formula", OFFSET(color_eval), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D}, + {"video_stream", "Make demuxer output a video stream", OFFSET(video_stream), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, + {"video_stream_w", "Video stream width in char (one char = 8x8px)", OFFSET(w), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_h", "Video stream height in char (one char = 8x8px)", OFFSET(h), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, + {"video_stream_ptxt", "Print speed, tempo, order, ... in video stream", OFFSET(print_textinfo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, + {NULL}, +}; + +#define SET_OPT_IF_REQUESTED(libopt, opt, flag) do { \ + if (modplug->opt) { \ + settings.libopt = modplug->opt; \ + settings.mFlags |= flag; \ + } \ +} while (0) + +#define ADD_META_MULTIPLE_ENTRIES(entry_name, fname) do { \ + if (n_## entry_name ##s) { \ + unsigned i, n = 0; \ + \ + for (i = 0; i < n_## entry_name ##s; i++) { \ + char item_name[64] = {0}; \ + fname(f, i, item_name); \ + if (!*item_name) \ + continue; \ + if (n) \ + av_dict_set(&s->metadata, #entry_name, "\n", AV_DICT_APPEND); \ + av_dict_set(&s->metadata, #entry_name, item_name, AV_DICT_APPEND); \ + n++; \ + } \ + \ + extra = av_asprintf(", %u/%u " #entry_name "%s", \ + n, n_## entry_name ##s, n > 1 ? "s" : ""); \ + if (!extra) \ + return AVERROR(ENOMEM); \ + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_APPEND); \ + av_free(extra); \ + } \ +} while (0) + +static int modplug_load_metadata(AVFormatContext *s) +{ + ModPlugContext *modplug = s->priv_data; + ModPlugFile *f = modplug->f; + char *extra; + const char *name = ModPlug_GetName(f); + const char *msg = ModPlug_GetMessage(f); + + unsigned n_instruments = ModPlug_NumInstruments(f); + unsigned n_samples = ModPlug_NumSamples(f); + unsigned n_patterns = ModPlug_NumPatterns(f); + unsigned n_channels = ModPlug_NumChannels(f); + + if (name && *name) av_dict_set(&s->metadata, "name", name, 0); + if (msg && *msg) av_dict_set(&s->metadata, "message", msg, 0); + + extra = av_asprintf("%u pattern%s, %u channel%s", + n_patterns, n_patterns > 1 ? "s" : "", + n_channels, n_channels > 1 ? "s" : ""); + if (!extra) + return AVERROR(ENOMEM); + av_dict_set(&s->metadata, "extra info", extra, AV_DICT_DONT_STRDUP_VAL); + + ADD_META_MULTIPLE_ENTRIES(instrument, ModPlug_InstrumentName); + ADD_META_MULTIPLE_ENTRIES(sample, ModPlug_SampleName); + + return 0; +} + +#define AUDIO_PKT_SIZE 512 + +static int modplug_read_header(AVFormatContext *s) +{ + AVStream *st; + AVIOContext *pb = s->pb; + ModPlug_Settings settings; + ModPlugContext *modplug = s->priv_data; + int sz = avio_size(pb); + + if (sz < 0) { + av_log(s, AV_LOG_WARNING, "Could not determine file size\n"); + sz = modplug->max_size; + } else if (modplug->max_size && sz > modplug->max_size) { + sz = modplug->max_size; + av_log(s, AV_LOG_WARNING, "Max file size reach%s, allocating %dB " + "but demuxing is likely to fail due to incomplete buffer\n", + sz == FF_MODPLUG_DEF_FILE_SIZE ? " (see -max_size)" : "", sz); + } + + if (modplug->color_eval) { + int r = av_expr_parse(&modplug->expr, modplug->color_eval, var_names, + NULL, NULL, NULL, NULL, 0, s); + if (r < 0) + return r; + } + + modplug->buf = av_malloc(modplug->max_size); + if (!modplug->buf) + return AVERROR(ENOMEM); + sz = avio_read(pb, modplug->buf, sz); + + ModPlug_GetSettings(&settings); + settings.mChannels = 2; + settings.mBits = 16; + settings.mFrequency = 44100; + settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; // best quality + settings.mLoopCount = 0; // prevents looping forever + + if (modplug->noise_reduction) settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION; + SET_OPT_IF_REQUESTED(mReverbDepth, reverb_depth, MODPLUG_ENABLE_REVERB); + SET_OPT_IF_REQUESTED(mReverbDelay, reverb_delay, MODPLUG_ENABLE_REVERB); + SET_OPT_IF_REQUESTED(mBassAmount, bass_amount, MODPLUG_ENABLE_MEGABASS); + SET_OPT_IF_REQUESTED(mBassRange, bass_range, MODPLUG_ENABLE_MEGABASS); + SET_OPT_IF_REQUESTED(mSurroundDepth, surround_depth, MODPLUG_ENABLE_SURROUND); + SET_OPT_IF_REQUESTED(mSurroundDelay, surround_delay, MODPLUG_ENABLE_SURROUND); + + if (modplug->reverb_depth) settings.mReverbDepth = modplug->reverb_depth; + if (modplug->reverb_delay) settings.mReverbDelay = modplug->reverb_delay; + if (modplug->bass_amount) settings.mBassAmount = modplug->bass_amount; + if (modplug->bass_range) settings.mBassRange = modplug->bass_range; + if (modplug->surround_depth) settings.mSurroundDepth = modplug->surround_depth; + if (modplug->surround_delay) settings.mSurroundDelay = modplug->surround_delay; + + ModPlug_SetSettings(&settings); + + modplug->f = ModPlug_Load(modplug->buf, sz); + if (!modplug->f) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 1000); + st->duration = ModPlug_GetLength(modplug->f); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + st->codec->channels = settings.mChannels; + st->codec->sample_rate = settings.mFrequency; + + // timebase = 1/1000, 2ch 16bits 44.1kHz-> 2*2*44100 + modplug->ts_per_packet = 1000*AUDIO_PKT_SIZE / (4*44100.); + + if (modplug->video_stream) { + AVStream *vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + avpriv_set_pts_info(vst, 64, 1, 1000); + vst->duration = st->duration; + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_XBIN; + vst->codec->width = modplug->w << 3; + vst->codec->height = modplug->h << 3; + modplug->linesize = modplug->w * 3; + modplug->fsize = modplug->linesize * modplug->h; + } + + return modplug_load_metadata(s); +} + +static void write_text(uint8_t *dst, const char *s, int linesize, int x, int y) +{ + int i; + dst += y*linesize + x*3; + for (i = 0; s[i]; i++, dst += 3) { + dst[0] = 0x0; // count - 1 + dst[1] = s[i]; // char + dst[2] = 0x0f; // background / foreground + } +} + +#define PRINT_INFO(line, name, idvalue) do { \ + snprintf(intbuf, sizeof(intbuf), "%.0f", var_values[idvalue]); \ + write_text(pkt->data, name ":", modplug->linesize, 0+1, line+1); \ + write_text(pkt->data, intbuf, modplug->linesize, 10+1, line+1); \ +} while (0) + +static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ModPlugContext *modplug = s->priv_data; + + if (modplug->video_stream) { + modplug->video_switch ^= 1; // one video packet for one audio packet + if (modplug->video_switch) { + double var_values[VAR_VARS_NB]; + + var_values[VAR_W ] = modplug->w; + var_values[VAR_H ] = modplug->h; + var_values[VAR_TIME ] = modplug->packet_count * modplug->ts_per_packet; + var_values[VAR_SPEED ] = ModPlug_GetCurrentSpeed (modplug->f); + var_values[VAR_TEMPO ] = ModPlug_GetCurrentTempo (modplug->f); + var_values[VAR_ORDER ] = ModPlug_GetCurrentOrder (modplug->f); + var_values[VAR_PATTERN] = ModPlug_GetCurrentPattern(modplug->f); + var_values[VAR_ROW ] = ModPlug_GetCurrentRow (modplug->f); + + if (av_new_packet(pkt, modplug->fsize) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 1; + memset(pkt->data, 0, modplug->fsize); + + if (modplug->print_textinfo) { + char intbuf[32]; + PRINT_INFO(0, "speed", VAR_SPEED); + PRINT_INFO(1, "tempo", VAR_TEMPO); + PRINT_INFO(2, "order", VAR_ORDER); + PRINT_INFO(3, "pattern", VAR_PATTERN); + PRINT_INFO(4, "row", VAR_ROW); + PRINT_INFO(5, "ts", VAR_TIME); + } + + if (modplug->expr) { + int x, y; + for (y = 0; y < modplug->h; y++) { + for (x = 0; x < modplug->w; x++) { + double color; + var_values[VAR_X] = x; + var_values[VAR_Y] = y; + color = av_expr_eval(modplug->expr, var_values, NULL); + pkt->data[y*modplug->linesize + x*3 + 2] |= av_clip((int)color, 0, 0xf)<<4; + } + } + } + pkt->pts = pkt->dts = var_values[VAR_TIME]; + pkt->flags |= AV_PKT_FLAG_KEY; + return 0; + } + } + + if (av_new_packet(pkt, AUDIO_PKT_SIZE) < 0) + return AVERROR(ENOMEM); + + if (modplug->video_stream) + pkt->pts = pkt->dts = modplug->packet_count++ * modplug->ts_per_packet; + + pkt->size = ModPlug_Read(modplug->f, pkt->data, AUDIO_PKT_SIZE); + if (pkt->size <= 0) { + av_free_packet(pkt); + return pkt->size == 0 ? AVERROR_EOF : AVERROR(EIO); + } + return 0; +} + +static int modplug_read_close(AVFormatContext *s) +{ + ModPlugContext *modplug = s->priv_data; + ModPlug_Unload(modplug->f); + av_freep(&modplug->buf); + return 0; +} + +static int modplug_read_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags) +{ + ModPlugContext *modplug = s->priv_data; + ModPlug_Seek(modplug->f, (int)ts); + if (modplug->video_stream) + modplug->packet_count = ts / modplug->ts_per_packet; + return 0; +} + +static const AVClass modplug_class = { + .class_name = "ModPlug demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_libmodplug_demuxer = { + .name = "libmodplug", + .long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"), + .priv_data_size = sizeof(ModPlugContext), + .read_header = modplug_read_header, + .read_packet = modplug_read_packet, + .read_close = modplug_read_close, + .read_seek = modplug_read_seek, + .extensions = "669,abc,amf,ams,dbm,dmf,dsm,far,it,mdl,med,mid,mod,mt2,mtm,okt,psm,ptm,s3m,stm,ult,umx,xm" + ",itgz,itr,itz,mdgz,mdr,mdz,s3gz,s3r,s3z,xmgz,xmr,xmz", // compressed mods + .priv_class = &modplug_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libnut.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/libnut.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,322 @@ +/* + * NUT (de)muxing via libnut + * copyright (c) 2006 Oded Shimon + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * NUT demuxing and muxing via libnut. + * @author Oded Shimon + */ + +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include + +#define ID_STRING "nut/multimedia container" +#define ID_LENGTH (strlen(ID_STRING) + 1) + +typedef struct { + nut_context_tt * nut; + nut_stream_header_tt * s; +} NUTContext; + +static const AVCodecTag nut_tags[] = { + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MP3, MKTAG('m', 'p', '3', ' ') }, + { AV_CODEC_ID_VORBIS, MKTAG('v', 'r', 'b', 's') }, + { 0, 0 }, +}; + +#if CONFIG_LIBNUT_MUXER +static int av_write(void * h, size_t len, const uint8_t * buf) { + AVIOContext * bc = h; + avio_write(bc, buf, len); + //avio_flush(bc); + return len; +} + +static int nut_write_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_muxer_opts_tt mopts = { + .output = { + .priv = bc, + .write = av_write, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .write_index = 1, + .realtime_stream = 0, + .max_distance = 32768, + .fti = NULL, + }; + nut_stream_header_tt * s; + int i; + + priv->s = s = av_mallocz((avf->nb_streams + 1) * sizeof*s); + if(!s) + return AVERROR(ENOMEM); + + for (i = 0; i < avf->nb_streams; i++) { + AVCodecContext * codec = avf->streams[i]->codec; + int j; + int fourcc = 0; + int num, denom, ssize; + + s[i].type = codec->codec_type == AVMEDIA_TYPE_VIDEO ? NUT_VIDEO_CLASS : NUT_AUDIO_CLASS; + + if (codec->codec_tag) fourcc = codec->codec_tag; + else fourcc = ff_codec_get_tag(nut_tags, codec->codec_id); + + if (!fourcc) { + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) fourcc = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id); + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) fourcc = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id); + } + + s[i].fourcc_len = 4; + s[i].fourcc = av_malloc(s[i].fourcc_len); + for (j = 0; j < s[i].fourcc_len; j++) s[i].fourcc[j] = (fourcc >> (j*8)) & 0xFF; + + ff_parse_specific_params(codec, &num, &ssize, &denom); + avpriv_set_pts_info(avf->streams[i], 60, denom, num); + + s[i].time_base.num = denom; + s[i].time_base.den = num; + + s[i].fixed_fps = 0; + s[i].decode_delay = codec->has_b_frames; + s[i].codec_specific_len = codec->extradata_size; + s[i].codec_specific = codec->extradata; + + if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + s[i].width = codec->width; + s[i].height = codec->height; + s[i].sample_width = 0; + s[i].sample_height = 0; + s[i].colorspace_type = 0; + } else { + s[i].samplerate_num = codec->sample_rate; + s[i].samplerate_denom = 1; + s[i].channel_count = codec->channels; + } + } + + s[avf->nb_streams].type = -1; + priv->nut = nut_muxer_init(&mopts, s, NULL); + + return 0; +} + +static int nut_write_packet(AVFormatContext * avf, AVPacket * pkt) { + NUTContext * priv = avf->priv_data; + nut_packet_tt p; + + p.len = pkt->size; + p.stream = pkt->stream_index; + p.pts = pkt->pts; + p.flags = pkt->flags & AV_PKT_FLAG_KEY ? NUT_FLAG_KEY : 0; + p.next_pts = 0; + + nut_write_frame_reorder(priv->nut, &p, pkt->data); + + return 0; +} + +static int nut_write_trailer(AVFormatContext * avf) { + AVIOContext * bc = avf->pb; + NUTContext * priv = avf->priv_data; + int i; + + nut_muxer_uninit_reorder(priv->nut); + avio_flush(bc); + + for(i = 0; priv->s[i].type != -1; i++ ) av_freep(&priv->s[i].fourcc); + av_freep(&priv->s); + + return 0; +} + +AVOutputFormat ff_libnut_muxer = { + .name = "libnut", + .long_name = "nut format", + .mime_type = "video/x-nut", + .extensions = "nut", + .priv_data_size = sizeof(NUTContext), + .audio_codec = AV_CODEC_ID_VORBIS, + .video_codec = AV_CODEC_ID_MPEG4, + .write_header = nut_write_header, + .write_packet = nut_write_packet, + .write_trailer = nut_write_trailer, + .flags = AVFMT_GLOBALHEADER, +}; +#endif /* CONFIG_LIBNUT_MUXER */ + +static int nut_probe(AVProbeData *p) { + if (!memcmp(p->buf, ID_STRING, ID_LENGTH)) return AVPROBE_SCORE_MAX; + + return 0; +} + +static size_t av_read(void * h, size_t len, uint8_t * buf) { + AVIOContext * bc = h; + return avio_read(bc, buf, len); +} + +static off_t av_seek(void * h, long long pos, int whence) { + AVIOContext * bc = h; + if (whence == SEEK_END) { + pos = avio_size(bc) + pos; + whence = SEEK_SET; + } + return avio_seek(bc, pos, whence); +} + +static int nut_read_header(AVFormatContext * avf) { + NUTContext * priv = avf->priv_data; + AVIOContext * bc = avf->pb; + nut_demuxer_opts_tt dopts = { + .input = { + .priv = bc, + .seek = av_seek, + .read = av_read, + .eof = NULL, + .file_pos = 0, + }, + .alloc = { av_malloc, av_realloc, av_free }, + .read_index = 1, + .cache_syncpoints = 1, + }; + nut_context_tt * nut = priv->nut = nut_demuxer_init(&dopts); + nut_stream_header_tt * s; + int ret, i; + + if(!nut) + return -1; + + if ((ret = nut_read_headers(nut, &s, NULL))) { + av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret)); + nut_demuxer_uninit(nut); + priv->nut = NULL; + return -1; + } + + priv->s = s; + + for (i = 0; s[i].type != -1 && i < 2; i++) { + AVStream * st = avformat_new_stream(avf, NULL); + int j; + + for (j = 0; j < s[i].fourcc_len && j < 8; j++) st->codec->codec_tag |= s[i].fourcc[j]<<(j*8); + + st->codec->has_b_frames = s[i].decode_delay; + + st->codec->extradata_size = s[i].codec_specific_len; + if (st->codec->extradata_size) { + st->codec->extradata = av_mallocz(st->codec->extradata_size); + if(!st->codec->extradata){ + nut_demuxer_uninit(nut); + priv->nut = NULL; + return AVERROR(ENOMEM); + } + memcpy(st->codec->extradata, s[i].codec_specific, st->codec->extradata_size); + } + + avpriv_set_pts_info(avf->streams[i], 60, s[i].time_base.num, s[i].time_base.den); + st->start_time = 0; + st->duration = s[i].max_pts; + + st->codec->codec_id = ff_codec_get_id(nut_tags, st->codec->codec_tag); + + switch(s[i].type) { + case NUT_AUDIO_CLASS: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, st->codec->codec_tag); + + st->codec->channels = s[i].channel_count; + st->codec->sample_rate = s[i].samplerate_num / s[i].samplerate_denom; + break; + case NUT_VIDEO_CLASS: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + if (st->codec->codec_id == AV_CODEC_ID_NONE) st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codec->codec_tag); + + st->codec->width = s[i].width; + st->codec->height = s[i].height; + st->sample_aspect_ratio.num = s[i].sample_width; + st->sample_aspect_ratio.den = s[i].sample_height; + break; + } + if (st->codec->codec_id == AV_CODEC_ID_NONE) av_log(avf, AV_LOG_ERROR, "Unknown codec?!\n"); + } + + return 0; +} + +static int nut_read_packet(AVFormatContext * avf, AVPacket * pkt) { + NUTContext * priv = avf->priv_data; + nut_packet_tt pd; + int ret; + + ret = nut_read_next_packet(priv->nut, &pd); + + if (ret || av_new_packet(pkt, pd.len) < 0) { + if (ret != NUT_ERR_EOF) + av_log(avf, AV_LOG_ERROR, " NUT error: %s\n", nut_error(ret)); + return -1; + } + + if (pd.flags & NUT_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pts = pd.pts; + pkt->stream_index = pd.stream; + pkt->pos = avio_tell(avf->pb); + + ret = nut_read_frame(priv->nut, &pd.len, pkt->data); + + return ret; +} + +static int nut_read_seek(AVFormatContext * avf, int stream_index, int64_t target_ts, int flags) { + NUTContext * priv = avf->priv_data; + int active_streams[] = { stream_index, -1 }; + double time_pos = target_ts * priv->s[stream_index].time_base.num / (double)priv->s[stream_index].time_base.den; + + if (nut_seek(priv->nut, time_pos, 2*!(flags & AVSEEK_FLAG_BACKWARD), active_streams)) return -1; + + return 0; +} + +static int nut_read_close(AVFormatContext *s) { + NUTContext * priv = s->priv_data; + + nut_demuxer_uninit(priv->nut); + + return 0; +} + +AVInputFormat ff_libnut_demuxer = { + .name = "libnut", + .long_name = NULL_IF_CONFIG_SMALL("NUT format"), + .priv_data_size = sizeof(NUTContext), + .read_probe = nut_probe, + .read_header = nut_read_header, + .read_packet = nut_read_packet, + .read_close = nut_read_close, + .read_seek = nut_read_seek, + .extensions = "nut", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/libquvi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/libquvi.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavformat/avformat.h" +#include "libavformat/internal.h" +#include "libavutil/opt.h" + +typedef struct { + const AVClass *class; + char *format; + AVFormatContext *fmtctx; +} LibQuviContext; + +#define OFFSET(x) offsetof(LibQuviContext, x) +#define FLAGS AV_OPT_FLAG_DECODING_PARAM +static const AVOption libquvi_options[] = { + { "format", "request specific format", OFFSET(format), AV_OPT_TYPE_STRING, {.str="best"}, .flags = FLAGS }, + { NULL } +}; + +static const AVClass libquvi_context_class = { + .class_name = "libquvi", + .item_name = av_default_item_name, + .option = libquvi_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int libquvi_close(AVFormatContext *s) +{ + LibQuviContext *qc = s->priv_data; + if (qc->fmtctx) + avformat_close_input(&qc->fmtctx); + return 0; +} + +static int libquvi_read_header(AVFormatContext *s) +{ + int i, ret; + quvi_t q; + quvi_media_t m; + QUVIcode rc; + LibQuviContext *qc = s->priv_data; + char *media_url, *pagetitle; + + rc = quvi_init(&q); + if (rc != QUVI_OK) + goto quvi_fail; + + quvi_setopt(q, QUVIOPT_FORMAT, qc->format); + + rc = quvi_parse(q, s->filename, &m); + if (rc != QUVI_OK) + goto quvi_fail; + + rc = quvi_getprop(m, QUVIPROP_MEDIAURL, &media_url); + if (rc != QUVI_OK) + goto quvi_fail; + + ret = avformat_open_input(&qc->fmtctx, media_url, NULL, NULL); + if (ret < 0) + goto end; + + rc = quvi_getprop(m, QUVIPROP_PAGETITLE, &pagetitle); + if (rc == QUVI_OK) + av_dict_set(&s->metadata, "title", pagetitle, 0); + + for (i = 0; i < qc->fmtctx->nb_streams; i++) { + AVStream *st = avformat_new_stream(s, NULL); + AVStream *ist = qc->fmtctx->streams[i]; + if (!st) { + ret = AVERROR(ENOMEM); + goto end; + } + avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den); + avcodec_copy_context(st->codec, qc->fmtctx->streams[i]->codec); + } + + return 0; + +quvi_fail: + av_log(s, AV_LOG_ERROR, "%s\n", quvi_strerror(q, rc)); + ret = AVERROR_EXTERNAL; + +end: + quvi_parse_close(&m); + quvi_close(&q); + return ret; +} + +static int libquvi_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + LibQuviContext *qc = s->priv_data; + return av_read_frame(qc->fmtctx, pkt); +} + +static int libquvi_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + LibQuviContext *qc = s->priv_data; + return av_seek_frame(qc->fmtctx, stream_index, timestamp, flags); +} + +static int libquvi_probe(AVProbeData *p) +{ + int score; + quvi_t q; + QUVIcode rc; + + rc = quvi_init(&q); + if (rc != QUVI_OK) + return AVERROR(ENOMEM); + score = quvi_supported(q, (char *)p->filename) == QUVI_OK ? AVPROBE_SCORE_MAX/2 : 0; + quvi_close(&q); + return score; +} + +AVInputFormat ff_libquvi_demuxer = { + .name = "libquvi", + .long_name = NULL_IF_CONFIG_SMALL("libquvi demuxer"), + .priv_data_size = sizeof(LibQuviContext), + .read_probe = libquvi_probe, + .read_header = libquvi_read_header, + .read_packet = libquvi_read_packet, + .read_close = libquvi_close, + .read_seek = libquvi_read_seek, + .priv_class = &libquvi_context_class, + .flags = AVFMT_NOFILE, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/librtmp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/librtmp.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,283 @@ +/* + * RTMP network protocol + * Copyright (c) 2010 Howard Chu + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * RTMP protocol based on http://rtmpdump.mplayerhq.hu/ librtmp + */ + +#include "libavutil/avstring.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "url.h" + +#include +#include + +typedef struct LibRTMPContext { + const AVClass *class; + RTMP rtmp; + char *app; + char *playpath; +} LibRTMPContext; + +static void rtmp_log(int level, const char *fmt, va_list args) +{ + switch (level) { + default: + case RTMP_LOGCRIT: level = AV_LOG_FATAL; break; + case RTMP_LOGERROR: level = AV_LOG_ERROR; break; + case RTMP_LOGWARNING: level = AV_LOG_WARNING; break; + case RTMP_LOGINFO: level = AV_LOG_INFO; break; + case RTMP_LOGDEBUG: level = AV_LOG_VERBOSE; break; + case RTMP_LOGDEBUG2: level = AV_LOG_DEBUG; break; + } + + av_vlog(NULL, level, fmt, args); + av_log(NULL, level, "\n"); +} + +static int rtmp_close(URLContext *s) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + RTMP_Close(r); + return 0; +} + +/** + * Open RTMP connection and verify that the stream can be played. + * + * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]... + * where 'app' is first one or two directories in the path + * (e.g. /ondemand/, /flash/live/, etc.) + * and 'playpath' is a file name (the rest of the path, + * may be prefixed with "mp4:") + * + * Additional RTMP library options may be appended as + * space-separated key-value pairs. + */ +static int rtmp_open(URLContext *s, const char *uri, int flags) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + int rc = 0, level; + char *filename = s->filename; + + switch (av_log_get_level()) { + default: + case AV_LOG_FATAL: level = RTMP_LOGCRIT; break; + case AV_LOG_ERROR: level = RTMP_LOGERROR; break; + case AV_LOG_WARNING: level = RTMP_LOGWARNING; break; + case AV_LOG_INFO: level = RTMP_LOGINFO; break; + case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG; break; + case AV_LOG_DEBUG: level = RTMP_LOGDEBUG2; break; + } + RTMP_LogSetLevel(level); + RTMP_LogSetCallback(rtmp_log); + + if (ctx->app || ctx->playpath) { + int len = strlen(s->filename) + 1; + if (ctx->app) len += strlen(ctx->app) + sizeof(" app="); + if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath="); + + if (!(filename = av_malloc(len))) + return AVERROR(ENOMEM); + + av_strlcpy(filename, s->filename, len); + if (ctx->app) { + av_strlcat(filename, " app=", len); + av_strlcat(filename, ctx->app, len); + } + if (ctx->playpath) { + av_strlcat(filename, " playpath=", len); + av_strlcat(filename, ctx->playpath, len); + } + } + + RTMP_Init(r); + if (!RTMP_SetupURL(r, filename)) { + rc = AVERROR_UNKNOWN; + goto fail; + } + + if (flags & AVIO_FLAG_WRITE) + RTMP_EnableWrite(r); + + if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) { + rc = AVERROR_UNKNOWN; + goto fail; + } + + s->is_streamed = 1; + rc = 0; +fail: + if (filename != s->filename) + av_freep(&filename); + return rc; +} + +static int rtmp_write(URLContext *s, const uint8_t *buf, int size) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + return RTMP_Write(r, buf, size); +} + +static int rtmp_read(URLContext *s, uint8_t *buf, int size) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + return RTMP_Read(r, buf, size); +} + +static int rtmp_read_pause(URLContext *s, int pause) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + if (!RTMP_Pause(r, pause)) + return AVERROR_UNKNOWN; + return 0; +} + +static int64_t rtmp_read_seek(URLContext *s, int stream_index, + int64_t timestamp, int flags) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + if (flags & AVSEEK_FLAG_BYTE) + return AVERROR(ENOSYS); + + /* seeks are in milliseconds */ + if (stream_index < 0) + timestamp = av_rescale_rnd(timestamp, 1000, AV_TIME_BASE, + flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); + + if (!RTMP_SendSeek(r, timestamp)) + return AVERROR_UNKNOWN; + return timestamp; +} + +static int rtmp_get_file_handle(URLContext *s) +{ + LibRTMPContext *ctx = s->priv_data; + RTMP *r = &ctx->rtmp; + + return RTMP_Socket(r); +} + +#define OFFSET(x) offsetof(LibRTMPContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, + {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, + { NULL }, +}; + +#define RTMP_CLASS(flavor)\ +static const AVClass lib ## flavor ## _class = {\ + .class_name = "lib" #flavor " protocol",\ + .item_name = av_default_item_name,\ + .option = options,\ + .version = LIBAVUTIL_VERSION_INT,\ +}; + +RTMP_CLASS(rtmp) +URLProtocol ff_librtmp_protocol = { + .name = "rtmp", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(LibRTMPContext), + .priv_data_class = &librtmp_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; + +RTMP_CLASS(rtmpt) +URLProtocol ff_librtmpt_protocol = { + .name = "rtmpt", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(LibRTMPContext), + .priv_data_class = &librtmpt_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; + +RTMP_CLASS(rtmpe) +URLProtocol ff_librtmpe_protocol = { + .name = "rtmpe", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(LibRTMPContext), + .priv_data_class = &librtmpe_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; + +RTMP_CLASS(rtmpte) +URLProtocol ff_librtmpte_protocol = { + .name = "rtmpte", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(LibRTMPContext), + .priv_data_class = &librtmpte_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; + +RTMP_CLASS(rtmps) +URLProtocol ff_librtmps_protocol = { + .name = "rtmps", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(LibRTMPContext), + .priv_data_class = &librtmps_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lmlm4.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lmlm4.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,127 @@ +/* + * Linux Media Labs MPEG-4 demuxer + * Copyright (c) 2008 Ivo van Poorten + * + * Due to a lack of sample files, only files with one channel are supported. + * u-law and ADPCM audio are unsupported for the same reason. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define LMLM4_I_FRAME 0x00 +#define LMLM4_P_FRAME 0x01 +#define LMLM4_B_FRAME 0x02 +#define LMLM4_INVALID 0x03 +#define LMLM4_MPEG1L2 0x04 + +#define LMLM4_MAX_PACKET_SIZE 1024 * 1024 + +static int lmlm4_probe(AVProbeData * pd) { + const unsigned char *buf = pd->buf; + unsigned int frame_type, packet_size; + + frame_type = AV_RB16(buf+2); + packet_size = AV_RB32(buf+4); + + if (!AV_RB16(buf) && frame_type <= LMLM4_MPEG1L2 && packet_size && + frame_type != LMLM4_INVALID && packet_size <= LMLM4_MAX_PACKET_SIZE) { + + if (frame_type == LMLM4_MPEG1L2) { + if ((AV_RB16(buf+8) & 0xfffe) != 0xfffc) + return 0; + /* I could calculate the audio framesize and compare with + * packet_size-8, but that seems overkill */ + return AVPROBE_SCORE_MAX / 3; + } else if (AV_RB24(buf+8) == 0x000001) { /* PES Signal */ + return AVPROBE_SCORE_MAX / 5; + } + } + + return 0; +} + +static int lmlm4_read_header(AVFormatContext *s) { + AVStream *st; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG4; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + avpriv_set_pts_info(st, 64, 1001, 30000); + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MP2; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + + /* the parameters will be extracted from the compressed bitstream */ + return 0; +} + +static int lmlm4_read_packet(AVFormatContext *s, AVPacket *pkt) { + AVIOContext *pb = s->pb; + int ret; + unsigned int frame_type, packet_size, padding, frame_size; + + avio_rb16(pb); /* channel number */ + frame_type = avio_rb16(pb); + packet_size = avio_rb32(pb); + padding = -packet_size & 511; + frame_size = packet_size - 8; + + if (frame_type > LMLM4_MPEG1L2 || frame_type == LMLM4_INVALID) { + av_log(s, AV_LOG_ERROR, "invalid or unsupported frame_type\n"); + return AVERROR(EIO); + } + if (packet_size > LMLM4_MAX_PACKET_SIZE || packet_size<=8) { + av_log(s, AV_LOG_ERROR, "packet size %d is invalid\n", packet_size); + return AVERROR(EIO); + } + + if ((ret = av_get_packet(pb, pkt, frame_size)) <= 0) + return AVERROR(EIO); + + avio_skip(pb, padding); + + switch (frame_type) { + case LMLM4_I_FRAME: + pkt->flags = AV_PKT_FLAG_KEY; + case LMLM4_P_FRAME: + case LMLM4_B_FRAME: + pkt->stream_index = 0; + break; + case LMLM4_MPEG1L2: + pkt->stream_index = 1; + break; + } + + return ret; +} + +AVInputFormat ff_lmlm4_demuxer = { + .name = "lmlm4", + .long_name = NULL_IF_CONFIG_SMALL("raw lmlm4"), + .read_probe = lmlm4_probe, + .read_header = lmlm4_read_header, + .read_packet = lmlm4_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lmlm4.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lmlm4.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/lmlm4.o libavformat/lmlm4.o: libavformat/lmlm4.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lmlm4.o Binary file ffmpeg/libavformat/lmlm4.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/loasdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/loasdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,87 @@ +/* + * LOAS AudioSyncStream demuxer + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/internal.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" + +static int loas_probe(AVProbeData *p) +{ + int max_frames = 0, first_frames = 0; + int fsize, frames; + const uint8_t *buf0 = p->buf; + const uint8_t *buf2; + const uint8_t *buf; + const uint8_t *end = buf0 + p->buf_size - 3; + buf = buf0; + + for(; buf < end; buf= buf2+1) { + buf2 = buf; + + for(frames = 0; buf2 < end; frames++) { + uint32_t header = AV_RB24(buf2); + if((header >> 13) != 0x2B7) + break; + fsize = (header & 0x1FFF) + 3; + if(fsize < 7) + break; + fsize = FFMIN(fsize, end - buf2); + buf2 += fsize; + } + max_frames = FFMAX(max_frames, frames); + if(buf == buf0) + first_frames= frames; + } + if (first_frames>=3) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>100)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=3) return AVPROBE_SCORE_MAX/4; + else return 0; +} + +static int loas_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + //LCM of all possible AAC sample rates + avpriv_set_pts_info(st, 64, 1, 28224000); + + return 0; +} + +AVInputFormat ff_loas_demuxer = { + .name = "loas", + .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"), + .read_probe = loas_probe, + .read_header = loas_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags= AVFMT_GENERIC_INDEX, + .raw_codec_id = AV_CODEC_ID_AAC_LATM, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/loasdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/loasdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/loasdec.o libavformat/loasdec.o: libavformat/loasdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/rawdec.h libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/loasdec.o Binary file ffmpeg/libavformat/loasdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/log2_tab.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/log2_tab.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1 @@ +#include "libavutil/log2_tab.c" diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lvfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lvfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,148 @@ +/* + * LVF demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "riff.h" + +static int lvf_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf) == MKTAG('L', 'V', 'F', 'F')) + return AVPROBE_SCORE_MAX / 2; + return 0; +} + +static int lvf_read_header(AVFormatContext *s) +{ + AVStream *st; + int64_t next_offset; + unsigned size, nb_streams, id; + + avio_skip(s->pb, 16); + nb_streams = avio_rl32(s->pb); + if (!nb_streams) + return AVERROR_INVALIDDATA; + if (nb_streams > 2) { + avpriv_request_sample(s, "%d streams", nb_streams); + return AVERROR_PATCHWELCOME; + } + + avio_skip(s->pb, 1012); + + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + next_offset = avio_tell(s->pb) + size; + + switch (id) { + case MKTAG('0', '0', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avio_skip(s->pb, 4); + st->codec->width = avio_rl32(s->pb); + st->codec->height = avio_rl32(s->pb); + avio_skip(s->pb, 4); + st->codec->codec_tag = avio_rl32(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case MKTAG('0', '1', 'f', 'm'): + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = avio_rl16(s->pb); + st->codec->channels = avio_rl16(s->pb); + st->codec->sample_rate = avio_rl16(s->pb); + avio_skip(s->pb, 8); + st->codec->bits_per_coded_sample = avio_r8(s->pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 32, 1, 1000); + break; + case 0: + avio_seek(s->pb, 2048 + 8, SEEK_SET); + return 0; + default: + avpriv_request_sample(s, "id %d", id); + return AVERROR_PATCHWELCOME; + } + + avio_seek(s->pb, next_offset, SEEK_SET); + } + + return AVERROR_EOF; +} + +static int lvf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + unsigned size, flags, timestamp, id; + int64_t pos; + int ret, is_video = 0; + + pos = avio_tell(s->pb); + while (!url_feof(s->pb)) { + id = avio_rl32(s->pb); + size = avio_rl32(s->pb); + + if (size == 0xFFFFFFFFu) + return AVERROR_EOF; + + switch (id) { + case MKTAG('0', '0', 'd', 'c'): + is_video = 1; + case MKTAG('0', '1', 'w', 'b'): + if (size < 8) + return AVERROR_INVALIDDATA; + timestamp = avio_rl32(s->pb); + flags = avio_rl32(s->pb); + ret = av_get_packet(s->pb, pkt, size - 8); + if (flags & (1 << 12)) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = is_video ? 0 : 1; + pkt->pts = timestamp; + pkt->pos = pos; + return ret; + default: + ret = avio_skip(s->pb, size); + } + + if (ret < 0) + return ret; + } + + return AVERROR_EOF; +} + +AVInputFormat ff_lvf_demuxer = { + .name = "lvf", + .long_name = NULL_IF_CONFIG_SMALL("LVF"), + .read_probe = lvf_probe, + .read_header = lvf_read_header, + .read_packet = lvf_read_packet, + .extensions = "lvf", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lvfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lvfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/lvfdec.o libavformat/lvfdec.o: libavformat/lvfdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/riff.h \ + libavformat/internal.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lvfdec.o Binary file ffmpeg/libavformat/lvfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lxfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lxfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,348 @@ +/* + * LXF demuxer + * Copyright (c) 2010 Tomas Härdin + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" + +#define LXF_MAX_PACKET_HEADER_SIZE 256 +#define LXF_HEADER_DATA_SIZE 120 +#define LXF_IDENT "LEITCH\0" +#define LXF_IDENT_LENGTH 8 +#define LXF_SAMPLERATE 48000 +#define LXF_MAX_AUDIO_PACKET (8008*15*4) ///< 15-channel 32-bit NTSC audio frame + +static const AVCodecTag lxf_tags[] = { + { AV_CODEC_ID_MJPEG, 0 }, + { AV_CODEC_ID_MPEG1VIDEO, 1 }, + { AV_CODEC_ID_MPEG2VIDEO, 2 }, //MpMl, 4:2:0 + { AV_CODEC_ID_MPEG2VIDEO, 3 }, //MpPl, 4:2:2 + { AV_CODEC_ID_DVVIDEO, 4 }, //DV25 + { AV_CODEC_ID_DVVIDEO, 5 }, //DVCPRO + { AV_CODEC_ID_DVVIDEO, 6 }, //DVCPRO50 + { AV_CODEC_ID_RAWVIDEO, 7 }, //AV_PIX_FMT_ARGB, where alpha is used for chroma keying + { AV_CODEC_ID_RAWVIDEO, 8 }, //16-bit chroma key + { AV_CODEC_ID_MPEG2VIDEO, 9 }, //4:2:2 CBP ("Constrained Bytes per Gop") + { AV_CODEC_ID_NONE, 0 }, +}; + +typedef struct { + int channels; ///< number of audio channels. zero means no audio + int frame_number; ///< current video frame + uint32_t video_format, packet_type, extended_size; +} LXFDemuxContext; + +static int lxf_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, LXF_IDENT, LXF_IDENT_LENGTH)) + return AVPROBE_SCORE_MAX; + + return 0; +} + +/** + * Verify the checksum of an LXF packet header + * + * @param[in] header the packet header to check + * @return zero if the checksum is OK, non-zero otherwise + */ +static int check_checksum(const uint8_t *header, int size) +{ + int x; + uint32_t sum = 0; + + for (x = 0; x < size; x += 4) + sum += AV_RL32(&header[x]); + + return sum; +} + +/** + * Read input until we find the next ident. If found, copy it to the header buffer + * + * @param[out] header where to copy the ident to + * @return 0 if an ident was found, < 0 on I/O error + */ +static int sync(AVFormatContext *s, uint8_t *header) +{ + uint8_t buf[LXF_IDENT_LENGTH]; + int ret; + + if ((ret = avio_read(s->pb, buf, LXF_IDENT_LENGTH)) != LXF_IDENT_LENGTH) + return ret < 0 ? ret : AVERROR_EOF; + + while (memcmp(buf, LXF_IDENT, LXF_IDENT_LENGTH)) { + if (url_feof(s->pb)) + return AVERROR_EOF; + + memmove(buf, &buf[1], LXF_IDENT_LENGTH-1); + buf[LXF_IDENT_LENGTH-1] = avio_r8(s->pb); + } + + memcpy(header, LXF_IDENT, LXF_IDENT_LENGTH); + + return 0; +} + +/** + * Read and checksum the next packet header + * + * @return the size of the payload following the header or < 0 on failure + */ +static int get_packet_header(AVFormatContext *s) +{ + LXFDemuxContext *lxf = s->priv_data; + AVIOContext *pb = s->pb; + int track_size, samples, ret; + uint32_t version, audio_format, header_size, channels, tmp; + AVStream *st; + uint8_t header[LXF_MAX_PACKET_HEADER_SIZE]; + const uint8_t *p; + + //find and read the ident + if ((ret = sync(s, header)) < 0) + return ret; + + ret = avio_read(pb, header + LXF_IDENT_LENGTH, 8); + if (ret != 8) + return ret < 0 ? ret : AVERROR_EOF; + + p = header + LXF_IDENT_LENGTH; + version = bytestream_get_le32(&p); + header_size = bytestream_get_le32(&p); + if (version > 1) + avpriv_request_sample(s, "format version %i", version); + if (header_size < (version ? 72 : 60) || + header_size > LXF_MAX_PACKET_HEADER_SIZE || + (header_size & 3)) { + av_log(s, AV_LOG_ERROR, "Invalid header size 0x%x\n", header_size); + return AVERROR_INVALIDDATA; + } + + //read the rest of the packet header + if ((ret = avio_read(pb, header + (p - header), + header_size - (p - header))) != + header_size - (p - header)) { + return ret < 0 ? ret : AVERROR_EOF; + } + + if (check_checksum(header, header_size)) + av_log(s, AV_LOG_ERROR, "checksum error\n"); + + lxf->packet_type = bytestream_get_le32(&p); + p += version ? 20 : 12; + + lxf->extended_size = 0; + switch (lxf->packet_type) { + case 0: + //video + lxf->video_format = bytestream_get_le32(&p); + ret = bytestream_get_le32(&p); + //skip VBI data and metadata + avio_skip(pb, (int64_t)(uint32_t)AV_RL32(p + 4) + + (int64_t)(uint32_t)AV_RL32(p + 12)); + break; + case 1: + //audio + if (!s->streams || !(st = s->streams[1])) { + av_log(s, AV_LOG_INFO, "got audio packet, but no audio stream present\n"); + break; + } + + if (version == 0) p += 8; + audio_format = bytestream_get_le32(&p); + channels = bytestream_get_le32(&p); + track_size = bytestream_get_le32(&p); + + //set codec based on specified audio bitdepth + //we only support tightly packed 16-, 20-, 24- and 32-bit PCM at the moment + st->codec->bits_per_coded_sample = (audio_format >> 6) & 0x3F; + + if (st->codec->bits_per_coded_sample != (audio_format & 0x3F)) { + av_log(s, AV_LOG_WARNING, "only tightly packed PCM currently supported\n"); + return AVERROR_PATCHWELCOME; + } + + switch (st->codec->bits_per_coded_sample) { + case 16: st->codec->codec_id = AV_CODEC_ID_PCM_S16LE_PLANAR; break; + case 20: st->codec->codec_id = AV_CODEC_ID_PCM_LXF; break; + case 24: st->codec->codec_id = AV_CODEC_ID_PCM_S24LE_PLANAR; break; + case 32: st->codec->codec_id = AV_CODEC_ID_PCM_S32LE_PLANAR; break; + default: + av_log(s, AV_LOG_WARNING, + "only 16-, 20-, 24- and 32-bit PCM currently supported\n"); + return AVERROR_PATCHWELCOME; + } + + samples = track_size * 8 / st->codec->bits_per_coded_sample; + + //use audio packet size to determine video standard + //for NTSC we have one 8008-sample audio frame per five video frames + if (samples == LXF_SAMPLERATE * 5005 / 30000) { + avpriv_set_pts_info(s->streams[0], 64, 1001, 30000); + } else { + //assume PAL, but warn if we don't have 1920 samples + if (samples != LXF_SAMPLERATE / 25) + av_log(s, AV_LOG_WARNING, + "video doesn't seem to be PAL or NTSC. guessing PAL\n"); + + avpriv_set_pts_info(s->streams[0], 64, 1, 25); + } + + //TODO: warning if track mask != (1 << channels) - 1? + ret = av_popcount(channels) * track_size; + + break; + default: + tmp = bytestream_get_le32(&p); + ret = bytestream_get_le32(&p); + if (tmp == 1) + lxf->extended_size = bytestream_get_le32(&p); + break; + } + + return ret; +} + +static int lxf_read_header(AVFormatContext *s) +{ + LXFDemuxContext *lxf = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t header_data[LXF_HEADER_DATA_SIZE]; + int ret; + AVStream *st; + uint32_t video_params, disk_params; + uint16_t record_date, expiration_date; + + if ((ret = get_packet_header(s)) < 0) + return ret; + + if (ret != LXF_HEADER_DATA_SIZE) { + av_log(s, AV_LOG_ERROR, "expected %d B size header, got %d\n", + LXF_HEADER_DATA_SIZE, ret); + return AVERROR_INVALIDDATA; + } + + if ((ret = avio_read(pb, header_data, LXF_HEADER_DATA_SIZE)) != LXF_HEADER_DATA_SIZE) + return ret < 0 ? ret : AVERROR_EOF; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + st->duration = AV_RL32(&header_data[32]); + video_params = AV_RL32(&header_data[40]); + record_date = AV_RL16(&header_data[56]); + expiration_date = AV_RL16(&header_data[58]); + disk_params = AV_RL32(&header_data[116]); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->bit_rate = 1000000 * ((video_params >> 14) & 0xFF); + st->codec->codec_tag = video_params & 0xF; + st->codec->codec_id = ff_codec_get_id(lxf_tags, st->codec->codec_tag); + + av_log(s, AV_LOG_DEBUG, "record: %x = %i-%02i-%02i\n", + record_date, 1900 + (record_date & 0x7F), (record_date >> 7) & 0xF, + (record_date >> 11) & 0x1F); + + av_log(s, AV_LOG_DEBUG, "expire: %x = %i-%02i-%02i\n", + expiration_date, 1900 + (expiration_date & 0x7F), (expiration_date >> 7) & 0xF, + (expiration_date >> 11) & 0x1F); + + if ((video_params >> 22) & 1) + av_log(s, AV_LOG_WARNING, "VBI data not yet supported\n"); + + if ((lxf->channels = 1 << (disk_params >> 4 & 3) + 1)) { + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->sample_rate = LXF_SAMPLERATE; + st->codec->channels = lxf->channels; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } + + avio_skip(s->pb, lxf->extended_size); + + return 0; +} + +static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + LXFDemuxContext *lxf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast = NULL; + uint32_t stream; + int ret, ret2; + + if ((ret = get_packet_header(s)) < 0) + return ret; + + stream = lxf->packet_type; + + if (stream > 1) { + av_log(s, AV_LOG_WARNING, "got packet with illegal stream index %u\n", stream); + return AVERROR(EAGAIN); + } + + if (stream == 1 && !(ast = s->streams[1])) { + av_log(s, AV_LOG_ERROR, "got audio packet without having an audio stream\n"); + return AVERROR_INVALIDDATA; + } + + //make sure the data fits in the de-planerization buffer + if (ast && ret > LXF_MAX_AUDIO_PACKET) { + av_log(s, AV_LOG_ERROR, "audio packet too large (%i > %i)\n", + ret, LXF_MAX_AUDIO_PACKET); + return AVERROR_INVALIDDATA; + } + + if ((ret2 = av_new_packet(pkt, ret)) < 0) + return ret2; + + if ((ret2 = avio_read(pb, pkt->data, ret)) != ret) { + av_free_packet(pkt); + return ret2 < 0 ? ret2 : AVERROR_EOF; + } + + pkt->stream_index = stream; + + if (!ast) { + //picture type (0 = closed I, 1 = open I, 2 = P, 3 = B) + if (((lxf->video_format >> 22) & 0x3) < 2) + pkt->flags |= AV_PKT_FLAG_KEY; + + pkt->dts = lxf->frame_number++; + } + + return ret; +} + +AVInputFormat ff_lxf_demuxer = { + .name = "lxf", + .long_name = NULL_IF_CONFIG_SMALL("VR native stream (LXF)"), + .priv_data_size = sizeof(LXFDemuxContext), + .read_probe = lxf_probe, + .read_header = lxf_read_header, + .read_packet = lxf_read_packet, + .codec_tag = (const AVCodecTag* const []){lxf_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lxfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/lxfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/lxfdec.o libavformat/lxfdec.o: libavformat/lxfdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavcodec/bytestream.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/lxfdec.o Binary file ffmpeg/libavformat/lxfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/m4vdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/m4vdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,52 @@ +/* + * RAW MPEG-4 video demuxer + * Copyright (c) 2006 Thijs Vermeir + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +#define VISUAL_OBJECT_START_CODE 0x000001b5 +#define VOP_START_CODE 0x000001b6 + +static int mpeg4video_probe(AVProbeData *probe_packet) +{ + uint32_t temp_buffer= -1; + int VO=0, VOL=0, VOP = 0, VISO = 0, res=0; + int i; + + for(i=0; ibuf_size; i++){ + temp_buffer = (temp_buffer<<8) + probe_packet->buf[i]; + if ((temp_buffer & 0xffffff00) != 0x100) + continue; + + if (temp_buffer == VOP_START_CODE) VOP++; + else if (temp_buffer == VISUAL_OBJECT_START_CODE) VISO++; + else if (temp_buffer < 0x120) VO++; + else if (temp_buffer < 0x130) VOL++; + else if ( !(0x1AF < temp_buffer && temp_buffer < 0x1B7) + && !(0x1B9 < temp_buffer && temp_buffer < 0x1C4)) res++; + } + + if (VOP >= VISO && VOP >= VOL && VO >= VOL && VOL > 0 && res==0) + return VOP+VO > 3 ? AVPROBE_SCORE_MAX/2 : AVPROBE_SCORE_MAX/4; + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(m4v, "raw MPEG-4 video", mpeg4video_probe, "m4v", AV_CODEC_ID_MPEG4) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/m4vdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/m4vdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/m4vdec.o libavformat/m4vdec.o: libavformat/m4vdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/m4vdec.o Binary file ffmpeg/libavformat/m4vdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroska.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroska.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,142 @@ +/* + * Matroska common data + * Copyright (c) 2003-2004 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "matroska.h" + +/* If you add a tag here that is not in ff_codec_bmp_tags[] + or ff_codec_wav_tags[], add it also to additional_audio_tags[] + or additional_video_tags[] in matroskaenc.c */ +const CodecTags ff_mkv_codec_tags[]={ + {"A_AAC" , AV_CODEC_ID_AAC}, + {"A_AC3" , AV_CODEC_ID_AC3}, + {"A_ALAC" , AV_CODEC_ID_ALAC}, + {"A_DTS" , AV_CODEC_ID_DTS}, + {"A_EAC3" , AV_CODEC_ID_EAC3}, + {"A_FLAC" , AV_CODEC_ID_FLAC}, + {"A_MLP" , AV_CODEC_ID_MLP}, + {"A_MPEG/L2" , AV_CODEC_ID_MP2}, + {"A_MPEG/L1" , AV_CODEC_ID_MP2}, + {"A_MPEG/L3" , AV_CODEC_ID_MP3}, + {"A_OPUS/EXPERIMENTAL",AV_CODEC_ID_OPUS}, + {"A_OPUS", AV_CODEC_ID_OPUS}, + {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F32LE}, + {"A_PCM/FLOAT/IEEE" , AV_CODEC_ID_PCM_F64LE}, + {"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S16BE}, + {"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S24BE}, + {"A_PCM/INT/BIG" , AV_CODEC_ID_PCM_S32BE}, + {"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_S16LE}, + {"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_S24LE}, + {"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_S32LE}, + {"A_PCM/INT/LIT" , AV_CODEC_ID_PCM_U8}, + {"A_QUICKTIME/QDM2" , AV_CODEC_ID_QDM2}, + {"A_REAL/14_4" , AV_CODEC_ID_RA_144}, + {"A_REAL/28_8" , AV_CODEC_ID_RA_288}, + {"A_REAL/ATRC" , AV_CODEC_ID_ATRAC3}, + {"A_REAL/COOK" , AV_CODEC_ID_COOK}, + {"A_REAL/SIPR" , AV_CODEC_ID_SIPR}, + {"A_TRUEHD" , AV_CODEC_ID_TRUEHD}, + {"A_TTA1" , AV_CODEC_ID_TTA}, + {"A_VORBIS" , AV_CODEC_ID_VORBIS}, + {"A_WAVPACK4" , AV_CODEC_ID_WAVPACK}, + + {"S_TEXT/UTF8" , AV_CODEC_ID_SUBRIP}, + {"S_TEXT/UTF8" , AV_CODEC_ID_TEXT}, + {"S_TEXT/UTF8" , AV_CODEC_ID_SRT}, + {"S_TEXT/ASCII" , AV_CODEC_ID_TEXT}, +#if FF_API_ASS_SSA + {"S_TEXT/ASS" , AV_CODEC_ID_SSA}, + {"S_TEXT/SSA" , AV_CODEC_ID_SSA}, + {"S_ASS" , AV_CODEC_ID_SSA}, + {"S_SSA" , AV_CODEC_ID_SSA}, +#endif + {"S_TEXT/ASS" , AV_CODEC_ID_ASS}, + {"S_TEXT/SSA" , AV_CODEC_ID_ASS}, + {"S_ASS" , AV_CODEC_ID_ASS}, + {"S_SSA" , AV_CODEC_ID_ASS}, + {"S_VOBSUB" , AV_CODEC_ID_DVD_SUBTITLE}, + {"S_DVBSUB" , AV_CODEC_ID_DVB_SUBTITLE}, + {"S_HDMV/PGS" , AV_CODEC_ID_HDMV_PGS_SUBTITLE}, + + {"V_DIRAC" , AV_CODEC_ID_DIRAC}, + {"V_MJPEG" , AV_CODEC_ID_MJPEG}, + {"V_MPEG1" , AV_CODEC_ID_MPEG1VIDEO}, + {"V_MPEG2" , AV_CODEC_ID_MPEG2VIDEO}, + {"V_MPEG4/ISO/ASP" , AV_CODEC_ID_MPEG4}, + {"V_MPEG4/ISO/AP" , AV_CODEC_ID_MPEG4}, + {"V_MPEG4/ISO/SP" , AV_CODEC_ID_MPEG4}, + {"V_MPEG4/ISO/AVC" , AV_CODEC_ID_H264}, + {"V_MPEG4/MS/V3" , AV_CODEC_ID_MSMPEG4V3}, + {"V_PRORES" , AV_CODEC_ID_PRORES}, + {"V_REAL/RV10" , AV_CODEC_ID_RV10}, + {"V_REAL/RV20" , AV_CODEC_ID_RV20}, + {"V_REAL/RV30" , AV_CODEC_ID_RV30}, + {"V_REAL/RV40" , AV_CODEC_ID_RV40}, + {"V_SNOW" , AV_CODEC_ID_SNOW}, + {"V_THEORA" , AV_CODEC_ID_THEORA}, + {"V_UNCOMPRESSED" , AV_CODEC_ID_RAWVIDEO}, + {"V_VP8" , AV_CODEC_ID_VP8}, + {"V_VP9" , AV_CODEC_ID_VP9}, + + {"" , AV_CODEC_ID_NONE} +}; + +const CodecMime ff_mkv_mime_tags[] = { + {"text/plain" , AV_CODEC_ID_TEXT}, + {"image/gif" , AV_CODEC_ID_GIF}, + {"image/jpeg" , AV_CODEC_ID_MJPEG}, + {"image/png" , AV_CODEC_ID_PNG}, + {"image/tiff" , AV_CODEC_ID_TIFF}, + {"application/x-truetype-font", AV_CODEC_ID_TTF}, + {"application/x-font" , AV_CODEC_ID_TTF}, + {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, + + {"" , AV_CODEC_ID_NONE} +}; + +const AVMetadataConv ff_mkv_metadata_conv[] = { + { "LEAD_PERFORMER", "performer" }, + { "PART_NUMBER" , "track" }, + { 0 } +}; + +const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT] = { + "mono", + "left_right", + "bottom_top", + "top_bottom", + "checkerboard_rl", + "checkerboard_lr", + "row_interleaved_rl", + "row_interleaved_lr", + "col_interleaved_rl", + "col_interleaved_lr", + "anaglyph_cyan_red", + "right_left", + "anaglyph_green_magenta", + "block_lr", + "block_rl", +}; + +const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = { + "left", + "right", + "background", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroska.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroska.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/matroska.o libavformat/matroska.o: libavformat/matroska.c \ + libavformat/matroska.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavformat/metadata.h \ + libavformat/avformat.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroska.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroska.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,280 @@ +/* + * Matroska constants + * Copyright (c) 2003-2004 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_MATROSKA_H +#define AVFORMAT_MATROSKA_H + +#include "libavcodec/avcodec.h" +#include "metadata.h" +#include "internal.h" + +/* EBML version supported */ +#define EBML_VERSION 1 + +/* top-level master-IDs */ +#define EBML_ID_HEADER 0x1A45DFA3 + +/* IDs in the HEADER master */ +#define EBML_ID_EBMLVERSION 0x4286 +#define EBML_ID_EBMLREADVERSION 0x42F7 +#define EBML_ID_EBMLMAXIDLENGTH 0x42F2 +#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3 +#define EBML_ID_DOCTYPE 0x4282 +#define EBML_ID_DOCTYPEVERSION 0x4287 +#define EBML_ID_DOCTYPEREADVERSION 0x4285 + +/* general EBML types */ +#define EBML_ID_VOID 0xEC +#define EBML_ID_CRC32 0xBF + +/* + * Matroska element IDs, max. 32 bits + */ + +/* toplevel segment */ +#define MATROSKA_ID_SEGMENT 0x18538067 + +/* Matroska top-level master IDs */ +#define MATROSKA_ID_INFO 0x1549A966 +#define MATROSKA_ID_TRACKS 0x1654AE6B +#define MATROSKA_ID_CUES 0x1C53BB6B +#define MATROSKA_ID_TAGS 0x1254C367 +#define MATROSKA_ID_SEEKHEAD 0x114D9B74 +#define MATROSKA_ID_ATTACHMENTS 0x1941A469 +#define MATROSKA_ID_CLUSTER 0x1F43B675 +#define MATROSKA_ID_CHAPTERS 0x1043A770 + +/* IDs in the info master */ +#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1 +#define MATROSKA_ID_DURATION 0x4489 +#define MATROSKA_ID_TITLE 0x7BA9 +#define MATROSKA_ID_WRITINGAPP 0x5741 +#define MATROSKA_ID_MUXINGAPP 0x4D80 +#define MATROSKA_ID_DATEUTC 0x4461 +#define MATROSKA_ID_SEGMENTUID 0x73A4 + +/* ID in the tracks master */ +#define MATROSKA_ID_TRACKENTRY 0xAE + +/* IDs in the trackentry master */ +#define MATROSKA_ID_TRACKNUMBER 0xD7 +#define MATROSKA_ID_TRACKUID 0x73C5 +#define MATROSKA_ID_TRACKTYPE 0x83 +#define MATROSKA_ID_TRACKVIDEO 0xE0 +#define MATROSKA_ID_TRACKAUDIO 0xE1 +#define MATROSKA_ID_TRACKOPERATION 0xE2 +#define MATROSKA_ID_TRACKCOMBINEPLANES 0xE3 +#define MATROSKA_ID_TRACKPLANE 0xE4 +#define MATROSKA_ID_TRACKPLANEUID 0xE5 +#define MATROSKA_ID_TRACKPLANETYPE 0xE6 +#define MATROSKA_ID_CODECID 0x86 +#define MATROSKA_ID_CODECPRIVATE 0x63A2 +#define MATROSKA_ID_CODECNAME 0x258688 +#define MATROSKA_ID_CODECINFOURL 0x3B4040 +#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 +#define MATROSKA_ID_CODECDECODEALL 0xAA +#define MATROSKA_ID_TRACKNAME 0x536E +#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C +#define MATROSKA_ID_TRACKFLAGENABLED 0xB9 +#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88 +#define MATROSKA_ID_TRACKFLAGFORCED 0x55AA +#define MATROSKA_ID_TRACKFLAGLACING 0x9C +#define MATROSKA_ID_TRACKMINCACHE 0x6DE7 +#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8 +#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383 +#define MATROSKA_ID_TRACKCONTENTENCODINGS 0x6D80 +#define MATROSKA_ID_TRACKCONTENTENCODING 0x6240 +#define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F +#define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE + +/* IDs in the trackvideo master */ +#define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3 +#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0 +#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA +#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0 +#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA +#define MATROSKA_ID_VIDEOPIXELCROPB 0x54AA +#define MATROSKA_ID_VIDEOPIXELCROPT 0x54BB +#define MATROSKA_ID_VIDEOPIXELCROPL 0x54CC +#define MATROSKA_ID_VIDEOPIXELCROPR 0x54DD +#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2 +#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A +#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B8 +#define MATROSKA_ID_VIDEOALPHAMODE 0x53C0 +#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3 +#define MATROSKA_ID_VIDEOCOLORSPACE 0x2EB524 + +/* IDs in the trackaudio master */ +#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5 +#define MATROSKA_ID_AUDIOOUTSAMPLINGFREQ 0x78B5 + +#define MATROSKA_ID_AUDIOBITDEPTH 0x6264 +#define MATROSKA_ID_AUDIOCHANNELS 0x9F + +/* IDs in the content encoding master */ +#define MATROSKA_ID_ENCODINGORDER 0x5031 +#define MATROSKA_ID_ENCODINGSCOPE 0x5032 +#define MATROSKA_ID_ENCODINGTYPE 0x5033 +#define MATROSKA_ID_ENCODINGCOMPRESSION 0x5034 +#define MATROSKA_ID_ENCODINGCOMPALGO 0x4254 +#define MATROSKA_ID_ENCODINGCOMPSETTINGS 0x4255 + +#define MATROSKA_ID_ENCODINGENCRYPTION 0x5035 +#define MATROSKA_ID_ENCODINGENCAESSETTINGS 0x47E7 +#define MATROSKA_ID_ENCODINGENCALGO 0x47E1 +#define MATROSKA_ID_ENCODINGENCKEYID 0x47E2 +#define MATROSKA_ID_ENCODINGSIGALGO 0x47E5 +#define MATROSKA_ID_ENCODINGSIGHASHALGO 0x47E6 +#define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4 +#define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3 + +/* ID in the cues master */ +#define MATROSKA_ID_POINTENTRY 0xBB + +/* IDs in the pointentry master */ +#define MATROSKA_ID_CUETIME 0xB3 +#define MATROSKA_ID_CUETRACKPOSITION 0xB7 + +/* IDs in the cuetrackposition master */ +#define MATROSKA_ID_CUETRACK 0xF7 +#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 +#define MATROSKA_ID_CUEBLOCKNUMBER 0x5378 + +/* IDs in the tags master */ +#define MATROSKA_ID_TAG 0x7373 +#define MATROSKA_ID_SIMPLETAG 0x67C8 +#define MATROSKA_ID_TAGNAME 0x45A3 +#define MATROSKA_ID_TAGSTRING 0x4487 +#define MATROSKA_ID_TAGLANG 0x447A +#define MATROSKA_ID_TAGDEFAULT 0x4484 +#define MATROSKA_ID_TAGDEFAULT_BUG 0x44B4 +#define MATROSKA_ID_TAGTARGETS 0x63C0 +#define MATROSKA_ID_TAGTARGETS_TYPE 0x63CA +#define MATROSKA_ID_TAGTARGETS_TYPEVALUE 0x68CA +#define MATROSKA_ID_TAGTARGETS_TRACKUID 0x63C5 +#define MATROSKA_ID_TAGTARGETS_CHAPTERUID 0x63C4 +#define MATROSKA_ID_TAGTARGETS_ATTACHUID 0x63C6 + +/* IDs in the seekhead master */ +#define MATROSKA_ID_SEEKENTRY 0x4DBB + +/* IDs in the seekpoint master */ +#define MATROSKA_ID_SEEKID 0x53AB +#define MATROSKA_ID_SEEKPOSITION 0x53AC + +/* IDs in the cluster master */ +#define MATROSKA_ID_CLUSTERTIMECODE 0xE7 +#define MATROSKA_ID_CLUSTERPOSITION 0xA7 +#define MATROSKA_ID_CLUSTERPREVSIZE 0xAB +#define MATROSKA_ID_BLOCKGROUP 0xA0 +#define MATROSKA_ID_BLOCKADDITIONS 0x75A1 +#define MATROSKA_ID_BLOCKMORE 0xA6 +#define MATROSKA_ID_BLOCKADDID 0xEE +#define MATROSKA_ID_BLOCKADDITIONAL 0xA5 +#define MATROSKA_ID_SIMPLEBLOCK 0xA3 + +/* IDs in the blockgroup master */ +#define MATROSKA_ID_BLOCK 0xA1 +#define MATROSKA_ID_BLOCKDURATION 0x9B +#define MATROSKA_ID_BLOCKREFERENCE 0xFB + +/* IDs in the attachments master */ +#define MATROSKA_ID_ATTACHEDFILE 0x61A7 +#define MATROSKA_ID_FILEDESC 0x467E +#define MATROSKA_ID_FILENAME 0x466E +#define MATROSKA_ID_FILEMIMETYPE 0x4660 +#define MATROSKA_ID_FILEDATA 0x465C +#define MATROSKA_ID_FILEUID 0x46AE + +/* IDs in the chapters master */ +#define MATROSKA_ID_EDITIONENTRY 0x45B9 +#define MATROSKA_ID_CHAPTERATOM 0xB6 +#define MATROSKA_ID_CHAPTERTIMESTART 0x91 +#define MATROSKA_ID_CHAPTERTIMEEND 0x92 +#define MATROSKA_ID_CHAPTERDISPLAY 0x80 +#define MATROSKA_ID_CHAPSTRING 0x85 +#define MATROSKA_ID_CHAPLANG 0x437C +#define MATROSKA_ID_EDITIONUID 0x45BC +#define MATROSKA_ID_EDITIONFLAGHIDDEN 0x45BD +#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB +#define MATROSKA_ID_EDITIONFLAGORDERED 0x45DD +#define MATROSKA_ID_CHAPTERUID 0x73C4 +#define MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98 +#define MATROSKA_ID_CHAPTERFLAGENABLED 0x4598 +#define MATROSKA_ID_CHAPTERPHYSEQUIV 0x63C3 + +typedef enum { + MATROSKA_TRACK_TYPE_NONE = 0x0, + MATROSKA_TRACK_TYPE_VIDEO = 0x1, + MATROSKA_TRACK_TYPE_AUDIO = 0x2, + MATROSKA_TRACK_TYPE_COMPLEX = 0x3, + MATROSKA_TRACK_TYPE_LOGO = 0x10, + MATROSKA_TRACK_TYPE_SUBTITLE = 0x11, + MATROSKA_TRACK_TYPE_CONTROL = 0x20, +} MatroskaTrackType; + +typedef enum { + MATROSKA_TRACK_ENCODING_COMP_ZLIB = 0, + MATROSKA_TRACK_ENCODING_COMP_BZLIB = 1, + MATROSKA_TRACK_ENCODING_COMP_LZO = 2, + MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP = 3, +} MatroskaTrackEncodingCompAlgo; + +typedef enum { + MATROSKA_VIDEO_STEREOMODE_TYPE_MONO = 0, + MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT = 1, + MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP = 2, + MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM = 3, + MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL = 4, + MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR = 5, + MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL = 6, + MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR = 7, + MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL = 8, + MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR = 9, + MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_CYAN_RED = 10, + MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT = 11, + MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_GREEN_MAG = 12, + MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR = 13, + MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL = 14, +} MatroskaVideoStereoModeType; + +/* + * Matroska Codec IDs, strings + */ + +typedef struct CodecTags{ + char str[20]; + enum AVCodecID id; +}CodecTags; + +/* max. depth in the EBML tree structure */ +#define EBML_MAX_DEPTH 16 + +#define MATROSKA_VIDEO_STEREO_MODE_COUNT 15 +#define MATROSKA_VIDEO_STEREO_PLANE_COUNT 3 + +extern const CodecTags ff_mkv_codec_tags[]; +extern const CodecMime ff_mkv_mime_tags[]; +extern const AVMetadataConv ff_mkv_metadata_conv[]; +extern const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREO_MODE_COUNT]; +extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT]; + +#endif /* AVFORMAT_MATROSKA_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroska.o Binary file ffmpeg/libavformat/matroska.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskadec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroskadec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,2571 @@ +/* + * Matroska file demuxer + * Copyright (c) 2003-2008 The FFmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Matroska file demuxer + * @author Ronald Bultje + * @author with a little help from Moritz Bunkus + * @author totally reworked by Aurelien Jacobs + * @see specs available on the Matroska project page: http://www.matroska.org/ + */ + +#include +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +/* For ff_codec_get_id(). */ +#include "riff.h" +#include "isom.h" +#include "rmsipr.h" +#include "matroska.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/mpeg4audio.h" +#include "libavutil/base64.h" +#include "libavutil/intfloat.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/avstring.h" +#include "libavutil/lzo.h" +#include "libavutil/dict.h" +#if CONFIG_ZLIB +#include +#endif +#if CONFIG_BZLIB +#include +#endif + +typedef enum { + EBML_NONE, + EBML_UINT, + EBML_FLOAT, + EBML_STR, + EBML_UTF8, + EBML_BIN, + EBML_NEST, + EBML_PASS, + EBML_STOP, + EBML_TYPE_COUNT +} EbmlType; + +typedef const struct EbmlSyntax { + uint32_t id; + EbmlType type; + int list_elem_size; + int data_offset; + union { + uint64_t u; + double f; + const char *s; + const struct EbmlSyntax *n; + } def; +} EbmlSyntax; + +typedef struct { + int nb_elem; + void *elem; +} EbmlList; + +typedef struct { + int size; + uint8_t *data; + int64_t pos; +} EbmlBin; + +typedef struct { + uint64_t version; + uint64_t max_size; + uint64_t id_length; + char *doctype; + uint64_t doctype_version; +} Ebml; + +typedef struct { + uint64_t algo; + EbmlBin settings; +} MatroskaTrackCompression; + +typedef struct { + uint64_t algo; + EbmlBin key_id; +} MatroskaTrackEncryption; + +typedef struct { + uint64_t scope; + uint64_t type; + MatroskaTrackCompression compression; + MatroskaTrackEncryption encryption; +} MatroskaTrackEncoding; + +typedef struct { + double frame_rate; + uint64_t display_width; + uint64_t display_height; + uint64_t pixel_width; + uint64_t pixel_height; + EbmlBin color_space; + uint64_t stereo_mode; + uint64_t alpha_mode; +} MatroskaTrackVideo; + +typedef struct { + double samplerate; + double out_samplerate; + uint64_t bitdepth; + uint64_t channels; + + /* real audio header (extracted from extradata) */ + int coded_framesize; + int sub_packet_h; + int frame_size; + int sub_packet_size; + int sub_packet_cnt; + int pkt_cnt; + uint64_t buf_timecode; + uint8_t *buf; +} MatroskaTrackAudio; + +typedef struct { + uint64_t uid; + uint64_t type; +} MatroskaTrackPlane; + +typedef struct { + EbmlList combine_planes; +} MatroskaTrackOperation; + +typedef struct { + uint64_t num; + uint64_t uid; + uint64_t type; + char *name; + char *codec_id; + EbmlBin codec_priv; + char *language; + double time_scale; + uint64_t default_duration; + uint64_t flag_default; + uint64_t flag_forced; + MatroskaTrackVideo video; + MatroskaTrackAudio audio; + MatroskaTrackOperation operation; + EbmlList encodings; + + AVStream *stream; + int64_t end_timecode; + int ms_compat; + uint64_t max_block_additional_id; +} MatroskaTrack; + +typedef struct { + uint64_t uid; + char *filename; + char *mime; + EbmlBin bin; + + AVStream *stream; +} MatroskaAttachement; + +typedef struct { + uint64_t start; + uint64_t end; + uint64_t uid; + char *title; + + AVChapter *chapter; +} MatroskaChapter; + +typedef struct { + uint64_t track; + uint64_t pos; +} MatroskaIndexPos; + +typedef struct { + uint64_t time; + EbmlList pos; +} MatroskaIndex; + +typedef struct { + char *name; + char *string; + char *lang; + uint64_t def; + EbmlList sub; +} MatroskaTag; + +typedef struct { + char *type; + uint64_t typevalue; + uint64_t trackuid; + uint64_t chapteruid; + uint64_t attachuid; +} MatroskaTagTarget; + +typedef struct { + MatroskaTagTarget target; + EbmlList tag; +} MatroskaTags; + +typedef struct { + uint64_t id; + uint64_t pos; +} MatroskaSeekhead; + +typedef struct { + uint64_t start; + uint64_t length; +} MatroskaLevel; + +typedef struct { + uint64_t timecode; + EbmlList blocks; +} MatroskaCluster; + +typedef struct { + AVFormatContext *ctx; + + /* EBML stuff */ + int num_levels; + MatroskaLevel levels[EBML_MAX_DEPTH]; + int level_up; + uint32_t current_id; + + uint64_t time_scale; + double duration; + char *title; + EbmlBin date_utc; + EbmlList tracks; + EbmlList attachments; + EbmlList chapters; + EbmlList index; + EbmlList tags; + EbmlList seekhead; + + /* byte position of the segment inside the stream */ + int64_t segment_start; + + /* the packet queue */ + AVPacket **packets; + int num_packets; + AVPacket *prev_pkt; + + int done; + + /* What to skip before effectively reading a packet. */ + int skip_to_keyframe; + uint64_t skip_to_timecode; + + /* File has a CUES element, but we defer parsing until it is needed. */ + int cues_parsing_deferred; + + int current_cluster_num_blocks; + int64_t current_cluster_pos; + MatroskaCluster current_cluster; + + /* File has SSA subtitles which prevent incremental cluster parsing. */ + int contains_ssa; +} MatroskaDemuxContext; + +typedef struct { + uint64_t duration; + int64_t reference; + uint64_t non_simple; + EbmlBin bin; + uint64_t additional_id; + EbmlBin additional; +} MatroskaBlock; + +static EbmlSyntax ebml_header[] = { + { EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml,version), {.u=EBML_VERSION} }, + { EBML_ID_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml,max_size), {.u=8} }, + { EBML_ID_EBMLMAXIDLENGTH, EBML_UINT, 0, offsetof(Ebml,id_length), {.u=4} }, + { EBML_ID_DOCTYPE, EBML_STR, 0, offsetof(Ebml,doctype), {.s="(none)"} }, + { EBML_ID_DOCTYPEREADVERSION, EBML_UINT, 0, offsetof(Ebml,doctype_version), {.u=1} }, + { EBML_ID_EBMLVERSION, EBML_NONE }, + { EBML_ID_DOCTYPEVERSION, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax ebml_syntax[] = { + { EBML_ID_HEADER, EBML_NEST, 0, 0, {.n=ebml_header} }, + { 0 } +}; + +static EbmlSyntax matroska_info[] = { + { MATROSKA_ID_TIMECODESCALE, EBML_UINT, 0, offsetof(MatroskaDemuxContext,time_scale), {.u=1000000} }, + { MATROSKA_ID_DURATION, EBML_FLOAT, 0, offsetof(MatroskaDemuxContext,duration) }, + { MATROSKA_ID_TITLE, EBML_UTF8, 0, offsetof(MatroskaDemuxContext,title) }, + { MATROSKA_ID_WRITINGAPP, EBML_NONE }, + { MATROSKA_ID_MUXINGAPP, EBML_NONE }, + { MATROSKA_ID_DATEUTC, EBML_BIN, 0, offsetof(MatroskaDemuxContext,date_utc) }, + { MATROSKA_ID_SEGMENTUID, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_video[] = { + { MATROSKA_ID_VIDEOFRAMERATE, EBML_FLOAT,0, offsetof(MatroskaTrackVideo,frame_rate) }, + { MATROSKA_ID_VIDEODISPLAYWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_width), {.u=-1} }, + { MATROSKA_ID_VIDEODISPLAYHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,display_height), {.u=-1} }, + { MATROSKA_ID_VIDEOPIXELWIDTH, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_width) }, + { MATROSKA_ID_VIDEOPIXELHEIGHT, EBML_UINT, 0, offsetof(MatroskaTrackVideo,pixel_height) }, + { MATROSKA_ID_VIDEOCOLORSPACE, EBML_BIN, 0, offsetof(MatroskaTrackVideo,color_space) }, + { MATROSKA_ID_VIDEOSTEREOMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,stereo_mode) }, + { MATROSKA_ID_VIDEOALPHAMODE, EBML_UINT, 0, offsetof(MatroskaTrackVideo,alpha_mode) }, + { MATROSKA_ID_VIDEOPIXELCROPB, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPT, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPL, EBML_NONE }, + { MATROSKA_ID_VIDEOPIXELCROPR, EBML_NONE }, + { MATROSKA_ID_VIDEODISPLAYUNIT, EBML_NONE }, + { MATROSKA_ID_VIDEOFLAGINTERLACED,EBML_NONE }, + { MATROSKA_ID_VIDEOASPECTRATIO, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_audio[] = { + { MATROSKA_ID_AUDIOSAMPLINGFREQ, EBML_FLOAT,0, offsetof(MatroskaTrackAudio,samplerate), {.f=8000.0} }, + { MATROSKA_ID_AUDIOOUTSAMPLINGFREQ,EBML_FLOAT,0,offsetof(MatroskaTrackAudio,out_samplerate) }, + { MATROSKA_ID_AUDIOBITDEPTH, EBML_UINT, 0, offsetof(MatroskaTrackAudio,bitdepth) }, + { MATROSKA_ID_AUDIOCHANNELS, EBML_UINT, 0, offsetof(MatroskaTrackAudio,channels), {.u=1} }, + { 0 } +}; + +static EbmlSyntax matroska_track_encoding_compression[] = { + { MATROSKA_ID_ENCODINGCOMPALGO, EBML_UINT, 0, offsetof(MatroskaTrackCompression,algo), {.u=0} }, + { MATROSKA_ID_ENCODINGCOMPSETTINGS,EBML_BIN, 0, offsetof(MatroskaTrackCompression,settings) }, + { 0 } +}; + +static EbmlSyntax matroska_track_encoding_encryption[] = { + { MATROSKA_ID_ENCODINGENCALGO, EBML_UINT, 0, offsetof(MatroskaTrackEncryption,algo), {.u=0} }, + { MATROSKA_ID_ENCODINGENCKEYID, EBML_BIN, 0, offsetof(MatroskaTrackEncryption,key_id) }, + { MATROSKA_ID_ENCODINGENCAESSETTINGS, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGALGO, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGHASHALGO, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGKEYID, EBML_NONE }, + { MATROSKA_ID_ENCODINGSIGNATURE, EBML_NONE }, + { 0 } +}; +static EbmlSyntax matroska_track_encoding[] = { + { MATROSKA_ID_ENCODINGSCOPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,scope), {.u=1} }, + { MATROSKA_ID_ENCODINGTYPE, EBML_UINT, 0, offsetof(MatroskaTrackEncoding,type), {.u=0} }, + { MATROSKA_ID_ENCODINGCOMPRESSION,EBML_NEST, 0, offsetof(MatroskaTrackEncoding,compression), {.n=matroska_track_encoding_compression} }, + { MATROSKA_ID_ENCODINGENCRYPTION, EBML_NEST, 0, offsetof(MatroskaTrackEncoding,encryption), {.n=matroska_track_encoding_encryption} }, + { MATROSKA_ID_ENCODINGORDER, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_track_encodings[] = { + { MATROSKA_ID_TRACKCONTENTENCODING, EBML_NEST, sizeof(MatroskaTrackEncoding), offsetof(MatroskaTrack,encodings), {.n=matroska_track_encoding} }, + { 0 } +}; + +static EbmlSyntax matroska_track_plane[] = { + { MATROSKA_ID_TRACKPLANEUID, EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) }, + { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) }, + { 0 } +}; + +static EbmlSyntax matroska_track_combine_planes[] = { + { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n=matroska_track_plane} }, + { 0 } +}; + +static EbmlSyntax matroska_track_operation[] = { + { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n=matroska_track_combine_planes} }, + { 0 } +}; + +static EbmlSyntax matroska_track[] = { + { MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack,num) }, + { MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack,name) }, + { MATROSKA_ID_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTrack,uid) }, + { MATROSKA_ID_TRACKTYPE, EBML_UINT, 0, offsetof(MatroskaTrack,type) }, + { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack,codec_id) }, + { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack,codec_priv) }, + { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack,language), {.s="eng"} }, + { MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack,default_duration) }, + { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT,0, offsetof(MatroskaTrack,time_scale), {.f=1.0} }, + { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack,flag_default), {.u=1} }, + { MATROSKA_ID_TRACKFLAGFORCED, EBML_UINT, 0, offsetof(MatroskaTrack,flag_forced), {.u=0} }, + { MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} }, + { MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack,audio), {.n=matroska_track_audio} }, + { MATROSKA_ID_TRACKOPERATION, EBML_NEST, 0, offsetof(MatroskaTrack,operation), {.n=matroska_track_operation} }, + { MATROSKA_ID_TRACKCONTENTENCODINGS,EBML_NEST, 0, 0, {.n=matroska_track_encodings} }, + { MATROSKA_ID_TRACKMAXBLKADDID, EBML_UINT, 0, offsetof(MatroskaTrack,max_block_additional_id) }, + { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, + { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, + { MATROSKA_ID_CODECNAME, EBML_NONE }, + { MATROSKA_ID_CODECDECODEALL, EBML_NONE }, + { MATROSKA_ID_CODECINFOURL, EBML_NONE }, + { MATROSKA_ID_CODECDOWNLOADURL, EBML_NONE }, + { MATROSKA_ID_TRACKMINCACHE, EBML_NONE }, + { MATROSKA_ID_TRACKMAXCACHE, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_tracks[] = { + { MATROSKA_ID_TRACKENTRY, EBML_NEST, sizeof(MatroskaTrack), offsetof(MatroskaDemuxContext,tracks), {.n=matroska_track} }, + { 0 } +}; + +static EbmlSyntax matroska_attachment[] = { + { MATROSKA_ID_FILEUID, EBML_UINT, 0, offsetof(MatroskaAttachement,uid) }, + { MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachement,filename) }, + { MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachement,mime) }, + { MATROSKA_ID_FILEDATA, EBML_BIN, 0, offsetof(MatroskaAttachement,bin) }, + { MATROSKA_ID_FILEDESC, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_attachments[] = { + { MATROSKA_ID_ATTACHEDFILE, EBML_NEST, sizeof(MatroskaAttachement), offsetof(MatroskaDemuxContext,attachments), {.n=matroska_attachment} }, + { 0 } +}; + +static EbmlSyntax matroska_chapter_display[] = { + { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter,title) }, + { MATROSKA_ID_CHAPLANG, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_chapter_entry[] = { + { MATROSKA_ID_CHAPTERTIMESTART, EBML_UINT, 0, offsetof(MatroskaChapter,start), {.u=AV_NOPTS_VALUE} }, + { MATROSKA_ID_CHAPTERTIMEEND, EBML_UINT, 0, offsetof(MatroskaChapter,end), {.u=AV_NOPTS_VALUE} }, + { MATROSKA_ID_CHAPTERUID, EBML_UINT, 0, offsetof(MatroskaChapter,uid) }, + { MATROSKA_ID_CHAPTERDISPLAY, EBML_NEST, 0, 0, {.n=matroska_chapter_display} }, + { MATROSKA_ID_CHAPTERFLAGHIDDEN, EBML_NONE }, + { MATROSKA_ID_CHAPTERFLAGENABLED, EBML_NONE }, + { MATROSKA_ID_CHAPTERPHYSEQUIV, EBML_NONE }, + { MATROSKA_ID_CHAPTERATOM, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_chapter[] = { + { MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext,chapters), {.n=matroska_chapter_entry} }, + { MATROSKA_ID_EDITIONUID, EBML_NONE }, + { MATROSKA_ID_EDITIONFLAGHIDDEN, EBML_NONE }, + { MATROSKA_ID_EDITIONFLAGDEFAULT, EBML_NONE }, + { MATROSKA_ID_EDITIONFLAGORDERED, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_chapters[] = { + { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, {.n=matroska_chapter} }, + { 0 } +}; + +static EbmlSyntax matroska_index_pos[] = { + { MATROSKA_ID_CUETRACK, EBML_UINT, 0, offsetof(MatroskaIndexPos,track) }, + { MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos,pos) }, + { MATROSKA_ID_CUEBLOCKNUMBER, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_index_entry[] = { + { MATROSKA_ID_CUETIME, EBML_UINT, 0, offsetof(MatroskaIndex,time) }, + { MATROSKA_ID_CUETRACKPOSITION, EBML_NEST, sizeof(MatroskaIndexPos), offsetof(MatroskaIndex,pos), {.n=matroska_index_pos} }, + { 0 } +}; + +static EbmlSyntax matroska_index[] = { + { MATROSKA_ID_POINTENTRY, EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext,index), {.n=matroska_index_entry} }, + { 0 } +}; + +static EbmlSyntax matroska_simpletag[] = { + { MATROSKA_ID_TAGNAME, EBML_UTF8, 0, offsetof(MatroskaTag,name) }, + { MATROSKA_ID_TAGSTRING, EBML_UTF8, 0, offsetof(MatroskaTag,string) }, + { MATROSKA_ID_TAGLANG, EBML_STR, 0, offsetof(MatroskaTag,lang), {.s="und"} }, + { MATROSKA_ID_TAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTag,def) }, + { MATROSKA_ID_TAGDEFAULT_BUG, EBML_UINT, 0, offsetof(MatroskaTag,def) }, + { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTag,sub), {.n=matroska_simpletag} }, + { 0 } +}; + +static EbmlSyntax matroska_tagtargets[] = { + { MATROSKA_ID_TAGTARGETS_TYPE, EBML_STR, 0, offsetof(MatroskaTagTarget,type) }, + { MATROSKA_ID_TAGTARGETS_TYPEVALUE, EBML_UINT, 0, offsetof(MatroskaTagTarget,typevalue), {.u=50} }, + { MATROSKA_ID_TAGTARGETS_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTagTarget,trackuid) }, + { MATROSKA_ID_TAGTARGETS_CHAPTERUID,EBML_UINT, 0, offsetof(MatroskaTagTarget,chapteruid) }, + { MATROSKA_ID_TAGTARGETS_ATTACHUID, EBML_UINT, 0, offsetof(MatroskaTagTarget,attachuid) }, + { 0 } +}; + +static EbmlSyntax matroska_tag[] = { + { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTags,tag), {.n=matroska_simpletag} }, + { MATROSKA_ID_TAGTARGETS, EBML_NEST, 0, offsetof(MatroskaTags,target), {.n=matroska_tagtargets} }, + { 0 } +}; + +static EbmlSyntax matroska_tags[] = { + { MATROSKA_ID_TAG, EBML_NEST, sizeof(MatroskaTags), offsetof(MatroskaDemuxContext,tags), {.n=matroska_tag} }, + { 0 } +}; + +static EbmlSyntax matroska_seekhead_entry[] = { + { MATROSKA_ID_SEEKID, EBML_UINT, 0, offsetof(MatroskaSeekhead,id) }, + { MATROSKA_ID_SEEKPOSITION, EBML_UINT, 0, offsetof(MatroskaSeekhead,pos), {.u=-1} }, + { 0 } +}; + +static EbmlSyntax matroska_seekhead[] = { + { MATROSKA_ID_SEEKENTRY, EBML_NEST, sizeof(MatroskaSeekhead), offsetof(MatroskaDemuxContext,seekhead), {.n=matroska_seekhead_entry} }, + { 0 } +}; + +static EbmlSyntax matroska_segment[] = { + { MATROSKA_ID_INFO, EBML_NEST, 0, 0, {.n=matroska_info } }, + { MATROSKA_ID_TRACKS, EBML_NEST, 0, 0, {.n=matroska_tracks } }, + { MATROSKA_ID_ATTACHMENTS, EBML_NEST, 0, 0, {.n=matroska_attachments} }, + { MATROSKA_ID_CHAPTERS, EBML_NEST, 0, 0, {.n=matroska_chapters } }, + { MATROSKA_ID_CUES, EBML_NEST, 0, 0, {.n=matroska_index } }, + { MATROSKA_ID_TAGS, EBML_NEST, 0, 0, {.n=matroska_tags } }, + { MATROSKA_ID_SEEKHEAD, EBML_NEST, 0, 0, {.n=matroska_seekhead } }, + { MATROSKA_ID_CLUSTER, EBML_STOP }, + { 0 } +}; + +static EbmlSyntax matroska_segments[] = { + { MATROSKA_ID_SEGMENT, EBML_NEST, 0, 0, {.n=matroska_segment } }, + { 0 } +}; + +static EbmlSyntax matroska_blockmore[] = { + { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) }, + { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, offsetof(MatroskaBlock,additional) }, + { 0 } +}; + +static EbmlSyntax matroska_blockadditions[] = { + { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, {.n=matroska_blockmore} }, + { 0 } +}; + +static EbmlSyntax matroska_blockgroup[] = { + { MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, + { MATROSKA_ID_BLOCKADDITIONS, EBML_NEST, 0, 0, {.n=matroska_blockadditions} }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, + { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration) }, + { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) }, + { 1, EBML_UINT, 0, offsetof(MatroskaBlock,non_simple), {.u=1} }, + { 0 } +}; + +static EbmlSyntax matroska_cluster[] = { + { MATROSKA_ID_CLUSTERTIMECODE,EBML_UINT,0, offsetof(MatroskaCluster,timecode) }, + { MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE }, + { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_clusters[] = { + { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, {.n=matroska_cluster} }, + { MATROSKA_ID_INFO, EBML_NONE }, + { MATROSKA_ID_CUES, EBML_NONE }, + { MATROSKA_ID_TAGS, EBML_NONE }, + { MATROSKA_ID_SEEKHEAD, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_cluster_incremental_parsing[] = { + { MATROSKA_ID_CLUSTERTIMECODE,EBML_UINT,0, offsetof(MatroskaCluster,timecode) }, + { MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster,blocks), {.n=matroska_blockgroup} }, + { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE }, + { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE }, + { MATROSKA_ID_INFO, EBML_NONE }, + { MATROSKA_ID_CUES, EBML_NONE }, + { MATROSKA_ID_TAGS, EBML_NONE }, + { MATROSKA_ID_SEEKHEAD, EBML_NONE }, + { MATROSKA_ID_CLUSTER, EBML_STOP }, + { 0 } +}; + +static EbmlSyntax matroska_cluster_incremental[] = { + { MATROSKA_ID_CLUSTERTIMECODE,EBML_UINT,0, offsetof(MatroskaCluster,timecode) }, + { MATROSKA_ID_BLOCKGROUP, EBML_STOP }, + { MATROSKA_ID_SIMPLEBLOCK, EBML_STOP }, + { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE }, + { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_clusters_incremental[] = { + { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, {.n=matroska_cluster_incremental} }, + { MATROSKA_ID_INFO, EBML_NONE }, + { MATROSKA_ID_CUES, EBML_NONE }, + { MATROSKA_ID_TAGS, EBML_NONE }, + { MATROSKA_ID_SEEKHEAD, EBML_NONE }, + { 0 } +}; + +static const char *const matroska_doctypes[] = { "matroska", "webm" }; + +static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos) +{ + AVIOContext *pb = matroska->ctx->pb; + uint32_t id; + matroska->current_id = 0; + matroska->num_levels = 0; + + // seek to next position to resync from + if (avio_seek(pb, last_pos + 1, SEEK_SET) < 0 || avio_tell(pb) <= last_pos) + goto eof; + + id = avio_rb32(pb); + + // try to find a toplevel element + while (!url_feof(pb)) { + if (id == MATROSKA_ID_INFO || id == MATROSKA_ID_TRACKS || + id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS || + id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS || + id == MATROSKA_ID_CLUSTER || id == MATROSKA_ID_CHAPTERS) + { + matroska->current_id = id; + return 0; + } + id = (id << 8) | avio_r8(pb); + } +eof: + matroska->done = 1; + return AVERROR_EOF; +} + +/* + * Return: Whether we reached the end of a level in the hierarchy or not. + */ +static int ebml_level_end(MatroskaDemuxContext *matroska) +{ + AVIOContext *pb = matroska->ctx->pb; + int64_t pos = avio_tell(pb); + + if (matroska->num_levels > 0) { + MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1]; + if (pos - level->start >= level->length || matroska->current_id) { + matroska->num_levels--; + return 1; + } + } + return 0; +} + +/* + * Read: an "EBML number", which is defined as a variable-length + * array of bytes. The first byte indicates the length by giving a + * number of 0-bits followed by a one. The position of the first + * "one" bit inside the first byte indicates the length of this + * number. + * Returns: number of bytes read, < 0 on error + */ +static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb, + int max_size, uint64_t *number) +{ + int read = 1, n = 1; + uint64_t total = 0; + + /* The first byte tells us the length in bytes - avio_r8() can normally + * return 0, but since that's not a valid first ebmlID byte, we can + * use it safely here to catch EOS. */ + if (!(total = avio_r8(pb))) { + /* we might encounter EOS here */ + if (!url_feof(pb)) { + int64_t pos = avio_tell(pb); + av_log(matroska->ctx, AV_LOG_ERROR, + "Read error at pos. %"PRIu64" (0x%"PRIx64")\n", + pos, pos); + return pb->error ? pb->error : AVERROR(EIO); + } + return AVERROR_EOF; + } + + /* get the length of the EBML number */ + read = 8 - ff_log2_tab[total]; + if (read > max_size) { + int64_t pos = avio_tell(pb) - 1; + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid EBML number size tag 0x%02x at pos %"PRIu64" (0x%"PRIx64")\n", + (uint8_t) total, pos, pos); + return AVERROR_INVALIDDATA; + } + + /* read out length */ + total ^= 1 << ff_log2_tab[total]; + while (n++ < read) + total = (total << 8) | avio_r8(pb); + + *number = total; + + return read; +} + +/** + * Read a EBML length value. + * This needs special handling for the "unknown length" case which has multiple + * encodings. + */ +static int ebml_read_length(MatroskaDemuxContext *matroska, AVIOContext *pb, + uint64_t *number) +{ + int res = ebml_read_num(matroska, pb, 8, number); + if (res > 0 && *number + 1 == 1ULL << (7 * res)) + *number = 0xffffffffffffffULL; + return res; +} + +/* + * Read the next element as an unsigned int. + * 0 is success, < 0 is failure. + */ +static int ebml_read_uint(AVIOContext *pb, int size, uint64_t *num) +{ + int n = 0; + + if (size > 8) + return AVERROR_INVALIDDATA; + + /* big-endian ordering; build up number */ + *num = 0; + while (n++ < size) + *num = (*num << 8) | avio_r8(pb); + + return 0; +} + +/* + * Read the next element as a float. + * 0 is success, < 0 is failure. + */ +static int ebml_read_float(AVIOContext *pb, int size, double *num) +{ + if (size == 0) { + *num = 0; + } else if (size == 4) { + *num = av_int2float(avio_rb32(pb)); + } else if (size == 8){ + *num = av_int2double(avio_rb64(pb)); + } else + return AVERROR_INVALIDDATA; + + return 0; +} + +/* + * Read the next element as an ASCII string. + * 0 is success, < 0 is failure. + */ +static int ebml_read_ascii(AVIOContext *pb, int size, char **str) +{ + char *res; + + /* EBML strings are usually not 0-terminated, so we allocate one + * byte more, read the string and NULL-terminate it ourselves. */ + if (!(res = av_malloc(size + 1))) + return AVERROR(ENOMEM); + if (avio_read(pb, (uint8_t *) res, size) != size) { + av_free(res); + return AVERROR(EIO); + } + (res)[size] = '\0'; + av_free(*str); + *str = res; + + return 0; +} + +/* + * Read the next element as binary data. + * 0 is success, < 0 is failure. + */ +static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin) +{ + av_fast_padded_malloc(&bin->data, &bin->size, length); + if (!bin->data) + return AVERROR(ENOMEM); + + bin->size = length; + bin->pos = avio_tell(pb); + if (avio_read(pb, bin->data, length) != length) { + av_freep(&bin->data); + bin->size = 0; + return AVERROR(EIO); + } + + return 0; +} + +/* + * Read the next element, but only the header. The contents + * are supposed to be sub-elements which can be read separately. + * 0 is success, < 0 is failure. + */ +static int ebml_read_master(MatroskaDemuxContext *matroska, uint64_t length) +{ + AVIOContext *pb = matroska->ctx->pb; + MatroskaLevel *level; + + if (matroska->num_levels >= EBML_MAX_DEPTH) { + av_log(matroska->ctx, AV_LOG_ERROR, + "File moves beyond max. allowed depth (%d)\n", EBML_MAX_DEPTH); + return AVERROR(ENOSYS); + } + + level = &matroska->levels[matroska->num_levels++]; + level->start = avio_tell(pb); + level->length = length; + + return 0; +} + +/* + * Read signed/unsigned "EBML" numbers. + * Return: number of bytes processed, < 0 on error + */ +static int matroska_ebmlnum_uint(MatroskaDemuxContext *matroska, + uint8_t *data, uint32_t size, uint64_t *num) +{ + AVIOContext pb; + ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL); + return ebml_read_num(matroska, &pb, FFMIN(size, 8), num); +} + +/* + * Same as above, but signed. + */ +static int matroska_ebmlnum_sint(MatroskaDemuxContext *matroska, + uint8_t *data, uint32_t size, int64_t *num) +{ + uint64_t unum; + int res; + + /* read as unsigned number first */ + if ((res = matroska_ebmlnum_uint(matroska, data, size, &unum)) < 0) + return res; + + /* make signed (weird way) */ + *num = unum - ((1LL << (7*res - 1)) - 1); + + return res; +} + +static int ebml_parse_elem(MatroskaDemuxContext *matroska, + EbmlSyntax *syntax, void *data); + +static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + uint32_t id, void *data) +{ + int i; + for (i=0; syntax[i].id; i++) + if (id == syntax[i].id) + break; + if (!syntax[i].id && id == MATROSKA_ID_CLUSTER && + matroska->num_levels > 0 && + matroska->levels[matroska->num_levels-1].length == 0xffffffffffffff) + return 0; // we reached the end of an unknown size cluster + if (!syntax[i].id && id != EBML_ID_VOID && id != EBML_ID_CRC32) { + av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%X\n", id); + if (matroska->ctx->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + return ebml_parse_elem(matroska, &syntax[i], data); +} + +static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + void *data) +{ + if (!matroska->current_id) { + uint64_t id; + int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id); + if (res < 0) + return res; + matroska->current_id = id | 1 << 7*res; + } + return ebml_parse_id(matroska, syntax, matroska->current_id, data); +} + +static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, + void *data) +{ + int i, res = 0; + + for (i=0; syntax[i].id; i++) + switch (syntax[i].type) { + case EBML_UINT: + *(uint64_t *)((char *)data+syntax[i].data_offset) = syntax[i].def.u; + break; + case EBML_FLOAT: + *(double *)((char *)data+syntax[i].data_offset) = syntax[i].def.f; + break; + case EBML_STR: + case EBML_UTF8: + *(char **)((char *)data+syntax[i].data_offset) = av_strdup(syntax[i].def.s); + break; + } + + while (!res && !ebml_level_end(matroska)) + res = ebml_parse(matroska, syntax, data); + + return res; +} + +static int ebml_parse_elem(MatroskaDemuxContext *matroska, + EbmlSyntax *syntax, void *data) +{ + static const uint64_t max_lengths[EBML_TYPE_COUNT] = { + [EBML_UINT] = 8, + [EBML_FLOAT] = 8, + // max. 16 MB for strings + [EBML_STR] = 0x1000000, + [EBML_UTF8] = 0x1000000, + // max. 256 MB for binary data + [EBML_BIN] = 0x10000000, + // no limits for anything else + }; + AVIOContext *pb = matroska->ctx->pb; + uint32_t id = syntax->id; + uint64_t length; + int res; + void *newelem; + + data = (char *)data + syntax->data_offset; + if (syntax->list_elem_size) { + EbmlList *list = data; + newelem = av_realloc(list->elem, (list->nb_elem+1)*syntax->list_elem_size); + if (!newelem) + return AVERROR(ENOMEM); + list->elem = newelem; + data = (char*)list->elem + list->nb_elem*syntax->list_elem_size; + memset(data, 0, syntax->list_elem_size); + list->nb_elem++; + } + + if (syntax->type != EBML_PASS && syntax->type != EBML_STOP) { + matroska->current_id = 0; + if ((res = ebml_read_length(matroska, pb, &length)) < 0) + return res; + if (max_lengths[syntax->type] && length > max_lengths[syntax->type]) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Invalid length 0x%"PRIx64" > 0x%"PRIx64" for syntax element %i\n", + length, max_lengths[syntax->type], syntax->type); + return AVERROR_INVALIDDATA; + } + } + + switch (syntax->type) { + case EBML_UINT: res = ebml_read_uint (pb, length, data); break; + case EBML_FLOAT: res = ebml_read_float (pb, length, data); break; + case EBML_STR: + case EBML_UTF8: res = ebml_read_ascii (pb, length, data); break; + case EBML_BIN: res = ebml_read_binary(pb, length, data); break; + case EBML_NEST: if ((res=ebml_read_master(matroska, length)) < 0) + return res; + if (id == MATROSKA_ID_SEGMENT) + matroska->segment_start = avio_tell(matroska->ctx->pb); + return ebml_parse_nest(matroska, syntax->def.n, data); + case EBML_PASS: return ebml_parse_id(matroska, syntax->def.n, id, data); + case EBML_STOP: return 1; + default: + if(ffio_limit(pb, length) != length) + return AVERROR(EIO); + return avio_skip(pb,length)<0 ? AVERROR(EIO) : 0; + } + if (res == AVERROR_INVALIDDATA) + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n"); + else if (res == AVERROR(EIO)) + av_log(matroska->ctx, AV_LOG_ERROR, "Read error\n"); + return res; +} + +static void ebml_free(EbmlSyntax *syntax, void *data) +{ + int i, j; + for (i=0; syntax[i].id; i++) { + void *data_off = (char *)data + syntax[i].data_offset; + switch (syntax[i].type) { + case EBML_STR: + case EBML_UTF8: av_freep(data_off); break; + case EBML_BIN: av_freep(&((EbmlBin *)data_off)->data); break; + case EBML_NEST: + if (syntax[i].list_elem_size) { + EbmlList *list = data_off; + char *ptr = list->elem; + for (j=0; jnb_elem; j++, ptr+=syntax[i].list_elem_size) + ebml_free(syntax[i].def.n, ptr); + av_free(list->elem); + } else + ebml_free(syntax[i].def.n, data_off); + default: break; + } + } +} + + +/* + * Autodetecting... + */ +static int matroska_probe(AVProbeData *p) +{ + uint64_t total = 0; + int len_mask = 0x80, size = 1, n = 1, i; + + /* EBML header? */ + if (AV_RB32(p->buf) != EBML_ID_HEADER) + return 0; + + /* length of header */ + total = p->buf[4]; + while (size <= 8 && !(total & len_mask)) { + size++; + len_mask >>= 1; + } + if (size > 8) + return 0; + total &= (len_mask - 1); + while (n < size) + total = (total << 8) | p->buf[4 + n++]; + + /* Does the probe data contain the whole header? */ + if (p->buf_size < 4 + size + total) + return 0; + + /* The header should contain a known document type. For now, + * we don't parse the whole header but simply check for the + * availability of that array of characters inside the header. + * Not fully fool-proof, but good enough. */ + for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) { + int probelen = strlen(matroska_doctypes[i]); + if (total < probelen) + continue; + for (n = 4+size; n <= 4+size+total-probelen; n++) + if (!memcmp(p->buf+n, matroska_doctypes[i], probelen)) + return AVPROBE_SCORE_MAX; + } + + // probably valid EBML header but no recognized doctype + return AVPROBE_SCORE_MAX/2; +} + +static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, + int num) +{ + MatroskaTrack *tracks = matroska->tracks.elem; + int i; + + for (i=0; i < matroska->tracks.nb_elem; i++) + if (tracks[i].num == num) + return &tracks[i]; + + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num); + return NULL; +} + +static int matroska_decode_buffer(uint8_t** buf, int* buf_size, + MatroskaTrack *track) +{ + MatroskaTrackEncoding *encodings = track->encodings.elem; + uint8_t* data = *buf; + int isize = *buf_size; + uint8_t* pkt_data = NULL; + uint8_t av_unused *newpktdata; + int pkt_size = isize; + int result = 0; + int olen; + + if (pkt_size >= 10000000U) + return AVERROR_INVALIDDATA; + + switch (encodings[0].compression.algo) { + case MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP: { + int header_size = encodings[0].compression.settings.size; + uint8_t *header = encodings[0].compression.settings.data; + + if (header_size && !header) { + av_log(NULL, AV_LOG_ERROR, "Compression size but no data in headerstrip\n"); + return -1; + } + + if (!header_size) + return 0; + + pkt_size = isize + header_size; + pkt_data = av_malloc(pkt_size); + if (!pkt_data) + return AVERROR(ENOMEM); + + memcpy(pkt_data, header, header_size); + memcpy(pkt_data + header_size, data, isize); + break; + } +#if CONFIG_LZO + case MATROSKA_TRACK_ENCODING_COMP_LZO: + do { + olen = pkt_size *= 3; + newpktdata = av_realloc(pkt_data, pkt_size + AV_LZO_OUTPUT_PADDING); + if (!newpktdata) { + result = AVERROR(ENOMEM); + goto failed; + } + pkt_data = newpktdata; + result = av_lzo1x_decode(pkt_data, &olen, data, &isize); + } while (result==AV_LZO_OUTPUT_FULL && pkt_size<10000000); + if (result) { + result = AVERROR_INVALIDDATA; + goto failed; + } + pkt_size -= olen; + break; +#endif +#if CONFIG_ZLIB + case MATROSKA_TRACK_ENCODING_COMP_ZLIB: { + z_stream zstream = {0}; + if (inflateInit(&zstream) != Z_OK) + return -1; + zstream.next_in = data; + zstream.avail_in = isize; + do { + pkt_size *= 3; + newpktdata = av_realloc(pkt_data, pkt_size); + if (!newpktdata) { + inflateEnd(&zstream); + goto failed; + } + pkt_data = newpktdata; + zstream.avail_out = pkt_size - zstream.total_out; + zstream.next_out = pkt_data + zstream.total_out; + if (pkt_data) { + result = inflate(&zstream, Z_NO_FLUSH); + } else + result = Z_MEM_ERROR; + } while (result==Z_OK && pkt_size<10000000); + pkt_size = zstream.total_out; + inflateEnd(&zstream); + if (result != Z_STREAM_END) { + if (result == Z_MEM_ERROR) + result = AVERROR(ENOMEM); + else + result = AVERROR_INVALIDDATA; + goto failed; + } + break; + } +#endif +#if CONFIG_BZLIB + case MATROSKA_TRACK_ENCODING_COMP_BZLIB: { + bz_stream bzstream = {0}; + if (BZ2_bzDecompressInit(&bzstream, 0, 0) != BZ_OK) + return -1; + bzstream.next_in = data; + bzstream.avail_in = isize; + do { + pkt_size *= 3; + newpktdata = av_realloc(pkt_data, pkt_size); + if (!newpktdata) { + BZ2_bzDecompressEnd(&bzstream); + goto failed; + } + pkt_data = newpktdata; + bzstream.avail_out = pkt_size - bzstream.total_out_lo32; + bzstream.next_out = pkt_data + bzstream.total_out_lo32; + if (pkt_data) { + result = BZ2_bzDecompress(&bzstream); + } else + result = BZ_MEM_ERROR; + } while (result==BZ_OK && pkt_size<10000000); + pkt_size = bzstream.total_out_lo32; + BZ2_bzDecompressEnd(&bzstream); + if (result != BZ_STREAM_END) { + if (result == BZ_MEM_ERROR) + result = AVERROR(ENOMEM); + else + result = AVERROR_INVALIDDATA; + goto failed; + } + break; + } +#endif + default: + return AVERROR_INVALIDDATA; + } + + *buf = pkt_data; + *buf_size = pkt_size; + return 0; + failed: + av_free(pkt_data); + return result; +} + +#if FF_API_ASS_SSA +static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska, + AVPacket *pkt, uint64_t display_duration) +{ + AVBufferRef *line; + char *layer, *ptr = pkt->data, *end = ptr+pkt->size; + for (; *ptr!=',' && ptrpts + display_duration; + int sc = matroska->time_scale * pkt->pts / 10000000; + int ec = matroska->time_scale * end_pts / 10000000; + int sh, sm, ss, eh, em, es, len; + sh = sc/360000; sc -= 360000*sh; + sm = sc/ 6000; sc -= 6000*sm; + ss = sc/ 100; sc -= 100*ss; + eh = ec/360000; ec -= 360000*eh; + em = ec/ 6000; ec -= 6000*em; + es = ec/ 100; ec -= 100*es; + *ptr++ = '\0'; + len = 50 + end-ptr + FF_INPUT_BUFFER_PADDING_SIZE; + if (!(line = av_buffer_alloc(len))) + return; + snprintf(line->data, len,"Dialogue: %s,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s\r\n", + layer, sh, sm, ss, sc, eh, em, es, ec, ptr); + av_buffer_unref(&pkt->buf); + pkt->buf = line; + pkt->data = line->data; + pkt->size = strlen(line->data); + } +} + +static int matroska_merge_packets(AVPacket *out, AVPacket *in) +{ + int ret = av_grow_packet(out, in->size); + if (ret < 0) + return ret; + + memcpy(out->data + out->size - in->size, in->data, in->size); + + av_free_packet(in); + av_free(in); + return 0; +} +#endif + +static void matroska_convert_tag(AVFormatContext *s, EbmlList *list, + AVDictionary **metadata, char *prefix) +{ + MatroskaTag *tags = list->elem; + char key[1024]; + int i; + + for (i=0; i < list->nb_elem; i++) { + const char *lang= (tags[i].lang && strcmp(tags[i].lang, "und")) ? tags[i].lang : NULL; + + if (!tags[i].name) { + av_log(s, AV_LOG_WARNING, "Skipping invalid tag with no TagName.\n"); + continue; + } + if (prefix) snprintf(key, sizeof(key), "%s/%s", prefix, tags[i].name); + else av_strlcpy(key, tags[i].name, sizeof(key)); + if (tags[i].def || !lang) { + av_dict_set(metadata, key, tags[i].string, 0); + if (tags[i].sub.nb_elem) + matroska_convert_tag(s, &tags[i].sub, metadata, key); + } + if (lang) { + av_strlcat(key, "-", sizeof(key)); + av_strlcat(key, lang, sizeof(key)); + av_dict_set(metadata, key, tags[i].string, 0); + if (tags[i].sub.nb_elem) + matroska_convert_tag(s, &tags[i].sub, metadata, key); + } + } + ff_metadata_conv(metadata, NULL, ff_mkv_metadata_conv); +} + +static void matroska_convert_tags(AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + MatroskaTags *tags = matroska->tags.elem; + int i, j; + + for (i=0; i < matroska->tags.nb_elem; i++) { + if (tags[i].target.attachuid) { + MatroskaAttachement *attachment = matroska->attachments.elem; + for (j=0; jattachments.nb_elem; j++) + if (attachment[j].uid == tags[i].target.attachuid + && attachment[j].stream) + matroska_convert_tag(s, &tags[i].tag, + &attachment[j].stream->metadata, NULL); + } else if (tags[i].target.chapteruid) { + MatroskaChapter *chapter = matroska->chapters.elem; + for (j=0; jchapters.nb_elem; j++) + if (chapter[j].uid == tags[i].target.chapteruid + && chapter[j].chapter) + matroska_convert_tag(s, &tags[i].tag, + &chapter[j].chapter->metadata, NULL); + } else if (tags[i].target.trackuid) { + MatroskaTrack *track = matroska->tracks.elem; + for (j=0; jtracks.nb_elem; j++) + if (track[j].uid == tags[i].target.trackuid && track[j].stream) + matroska_convert_tag(s, &tags[i].tag, + &track[j].stream->metadata, NULL); + } else { + matroska_convert_tag(s, &tags[i].tag, &s->metadata, + tags[i].target.type); + } + } +} + +static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, int idx) +{ + EbmlList *seekhead_list = &matroska->seekhead; + MatroskaSeekhead *seekhead = seekhead_list->elem; + uint32_t level_up = matroska->level_up; + int64_t before_pos = avio_tell(matroska->ctx->pb); + uint32_t saved_id = matroska->current_id; + MatroskaLevel level; + int64_t offset; + int ret = 0; + + if (idx >= seekhead_list->nb_elem + || seekhead[idx].id == MATROSKA_ID_SEEKHEAD + || seekhead[idx].id == MATROSKA_ID_CLUSTER) + return 0; + + /* seek */ + offset = seekhead[idx].pos + matroska->segment_start; + if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) { + /* We don't want to lose our seekhead level, so we add + * a dummy. This is a crude hack. */ + if (matroska->num_levels == EBML_MAX_DEPTH) { + av_log(matroska->ctx, AV_LOG_INFO, + "Max EBML element depth (%d) reached, " + "cannot parse further.\n", EBML_MAX_DEPTH); + ret = AVERROR_INVALIDDATA; + } else { + level.start = 0; + level.length = (uint64_t)-1; + matroska->levels[matroska->num_levels] = level; + matroska->num_levels++; + matroska->current_id = 0; + + ret = ebml_parse(matroska, matroska_segment, matroska); + + /* remove dummy level */ + while (matroska->num_levels) { + uint64_t length = matroska->levels[--matroska->num_levels].length; + if (length == (uint64_t)-1) + break; + } + } + } + /* seek back */ + avio_seek(matroska->ctx->pb, before_pos, SEEK_SET); + matroska->level_up = level_up; + matroska->current_id = saved_id; + + return ret; +} + +static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) +{ + EbmlList *seekhead_list = &matroska->seekhead; + int64_t before_pos = avio_tell(matroska->ctx->pb); + int i; + + // we should not do any seeking in the streaming case + if (!matroska->ctx->pb->seekable || + (matroska->ctx->flags & AVFMT_FLAG_IGNIDX)) + return; + + for (i = 0; i < seekhead_list->nb_elem; i++) { + MatroskaSeekhead *seekhead = seekhead_list->elem; + if (seekhead[i].pos <= before_pos) + continue; + + // defer cues parsing until we actually need cue data. + if (seekhead[i].id == MATROSKA_ID_CUES) { + matroska->cues_parsing_deferred = 1; + continue; + } + + if (matroska_parse_seekhead_entry(matroska, i) < 0) { + // mark index as broken + matroska->cues_parsing_deferred = -1; + break; + } + } +} + +static void matroska_add_index_entries(MatroskaDemuxContext *matroska) { + EbmlList *index_list; + MatroskaIndex *index; + int index_scale = 1; + int i, j; + + index_list = &matroska->index; + index = index_list->elem; + if (index_list->nb_elem + && index[0].time > 1E14/matroska->time_scale) { + av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n"); + index_scale = matroska->time_scale; + } + for (i = 0; i < index_list->nb_elem; i++) { + EbmlList *pos_list = &index[i].pos; + MatroskaIndexPos *pos = pos_list->elem; + for (j = 0; j < pos_list->nb_elem; j++) { + MatroskaTrack *track = matroska_find_track_by_num(matroska, pos[j].track); + if (track && track->stream) + av_add_index_entry(track->stream, + pos[j].pos + matroska->segment_start, + index[i].time/index_scale, 0, 0, + AVINDEX_KEYFRAME); + } + } +} + +static void matroska_parse_cues(MatroskaDemuxContext *matroska) { + EbmlList *seekhead_list = &matroska->seekhead; + MatroskaSeekhead *seekhead = seekhead_list->elem; + int i; + + for (i = 0; i < seekhead_list->nb_elem; i++) + if (seekhead[i].id == MATROSKA_ID_CUES) + break; + av_assert1(i <= seekhead_list->nb_elem); + + if (matroska_parse_seekhead_entry(matroska, i) < 0) + matroska->cues_parsing_deferred = -1; + matroska_add_index_entries(matroska); +} + +static int matroska_aac_profile(char *codec_id) +{ + static const char * const aac_profiles[] = { "MAIN", "LC", "SSR" }; + int profile; + + for (profile=0; profilepriv_data; + EbmlList *attachements_list = &matroska->attachments; + MatroskaAttachement *attachements; + EbmlList *chapters_list = &matroska->chapters; + MatroskaChapter *chapters; + MatroskaTrack *tracks; + uint64_t max_start = 0; + int64_t pos; + Ebml ebml = { 0 }; + AVStream *st; + int i, j, k, res; + + matroska->ctx = s; + + /* First read the EBML header. */ + if (ebml_parse(matroska, ebml_syntax, &ebml) + || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t) + || ebml.id_length > sizeof(uint32_t) || ebml.doctype_version > 3 || !ebml.doctype) { + av_log(matroska->ctx, AV_LOG_ERROR, + "EBML header using unsupported features\n" + "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", + ebml.version, ebml.doctype, ebml.doctype_version); + ebml_free(ebml_syntax, &ebml); + return AVERROR_PATCHWELCOME; + } else if (ebml.doctype_version == 3) { + av_log(matroska->ctx, AV_LOG_WARNING, + "EBML header using unsupported features\n" + "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n", + ebml.version, ebml.doctype, ebml.doctype_version); + } + for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) + if (!strcmp(ebml.doctype, matroska_doctypes[i])) + break; + if (i >= FF_ARRAY_ELEMS(matroska_doctypes)) { + av_log(s, AV_LOG_WARNING, "Unknown EBML doctype '%s'\n", ebml.doctype); + if (matroska->ctx->error_recognition & AV_EF_EXPLODE) { + ebml_free(ebml_syntax, &ebml); + return AVERROR_INVALIDDATA; + } + } + ebml_free(ebml_syntax, &ebml); + + /* The next thing is a segment. */ + pos = avio_tell(matroska->ctx->pb); + res = ebml_parse(matroska, matroska_segments, matroska); + // try resyncing until we find a EBML_STOP type element. + while (res != 1) { + res = matroska_resync(matroska, pos); + if (res < 0) + return res; + pos = avio_tell(matroska->ctx->pb); + res = ebml_parse(matroska, matroska_segment, matroska); + } + matroska_execute_seekhead(matroska); + + if (!matroska->time_scale) + matroska->time_scale = 1000000; + if (matroska->duration) + matroska->ctx->duration = matroska->duration * matroska->time_scale + * 1000 / AV_TIME_BASE; + av_dict_set(&s->metadata, "title", matroska->title, 0); + + if (matroska->date_utc.size == 8) + matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data)); + + tracks = matroska->tracks.elem; + for (i=0; i < matroska->tracks.nb_elem; i++) { + MatroskaTrack *track = &tracks[i]; + enum AVCodecID codec_id = AV_CODEC_ID_NONE; + EbmlList *encodings_list = &track->encodings; + MatroskaTrackEncoding *encodings = encodings_list->elem; + uint8_t *extradata = NULL; + int extradata_size = 0; + int extradata_offset = 0; + uint32_t fourcc = 0; + AVIOContext b; + char* key_id_base64 = NULL; + + /* Apply some sanity checks. */ + if (track->type != MATROSKA_TRACK_TYPE_VIDEO && + track->type != MATROSKA_TRACK_TYPE_AUDIO && + track->type != MATROSKA_TRACK_TYPE_SUBTITLE) { + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown or unsupported track type %"PRIu64"\n", + track->type); + continue; + } + if (track->codec_id == NULL) + continue; + + if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + if (!track->default_duration && track->video.frame_rate > 0) + track->default_duration = 1000000000/track->video.frame_rate; + if (track->video.display_width == -1) + track->video.display_width = track->video.pixel_width; + if (track->video.display_height == -1) + track->video.display_height = track->video.pixel_height; + if (track->video.color_space.size == 4) + fourcc = AV_RL32(track->video.color_space.data); + } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { + if (!track->audio.out_samplerate) + track->audio.out_samplerate = track->audio.samplerate; + } + if (encodings_list->nb_elem > 1) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Multiple combined encodings not supported"); + } else if (encodings_list->nb_elem == 1) { + if (encodings[0].type) { + if (encodings[0].encryption.key_id.size > 0) { + /* Save the encryption key id to be stored later as a + metadata tag. */ + const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size); + key_id_base64 = av_malloc(b64_size); + if (key_id_base64 == NULL) + return AVERROR(ENOMEM); + + av_base64_encode(key_id_base64, b64_size, + encodings[0].encryption.key_id.data, + encodings[0].encryption.key_id.size); + } else { + encodings[0].scope = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Unsupported encoding type"); + } + } else if ( +#if CONFIG_ZLIB + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB && +#endif +#if CONFIG_BZLIB + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_BZLIB && +#endif +#if CONFIG_LZO + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO && +#endif + encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) { + encodings[0].scope = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Unsupported encoding type"); + } else if (track->codec_priv.size && encodings[0].scope&2) { + uint8_t *codec_priv = track->codec_priv.data; + int ret = matroska_decode_buffer(&track->codec_priv.data, + &track->codec_priv.size, + track); + if (ret < 0) { + track->codec_priv.data = NULL; + track->codec_priv.size = 0; + av_log(matroska->ctx, AV_LOG_ERROR, + "Failed to decode codec private data\n"); + } + + if (codec_priv != track->codec_priv.data) + av_free(codec_priv); + } + } + + for(j=0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++){ + if(!strncmp(ff_mkv_codec_tags[j].str, track->codec_id, + strlen(ff_mkv_codec_tags[j].str))){ + codec_id= ff_mkv_codec_tags[j].id; + break; + } + } + + st = track->stream = avformat_new_stream(s, NULL); + if (st == NULL) { + av_free(key_id_base64); + return AVERROR(ENOMEM); + } + + if (key_id_base64) { + /* export encryption key id as base64 metadata tag */ + av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0); + av_freep(&key_id_base64); + } + + if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") + && track->codec_priv.size >= 40 + && track->codec_priv.data != NULL) { + track->ms_compat = 1; + fourcc = AV_RL32(track->codec_priv.data + 16); + codec_id = ff_codec_get_id(ff_codec_bmp_tags, fourcc); + extradata_offset = 40; + } else if (!strcmp(track->codec_id, "A_MS/ACM") + && track->codec_priv.size >= 14 + && track->codec_priv.data != NULL) { + int ret; + ffio_init_context(&b, track->codec_priv.data, track->codec_priv.size, + 0, NULL, NULL, NULL, NULL); + ret = ff_get_wav_header(&b, st->codec, track->codec_priv.size); + if (ret < 0) + return ret; + codec_id = st->codec->codec_id; + extradata_offset = FFMIN(track->codec_priv.size, 18); + } else if (!strcmp(track->codec_id, "V_QUICKTIME") + && (track->codec_priv.size >= 86) + && (track->codec_priv.data != NULL)) { + fourcc = AV_RL32(track->codec_priv.data); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); + } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - FF_INPUT_BUFFER_PADDING_SIZE) { + /* Only ALAC's magic cookie is stored in Matroska's track headers. + Create the "atom size", "tag", and "tag version" fields the + decoder expects manually. */ + extradata_size = 12 + track->codec_priv.size; + extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (extradata == NULL) + return AVERROR(ENOMEM); + AV_WB32(extradata, extradata_size); + memcpy(&extradata[4], "alac", 4); + AV_WB32(&extradata[8], 0); + memcpy(&extradata[12], track->codec_priv.data, track->codec_priv.size); + } else if (codec_id == AV_CODEC_ID_PCM_S16BE) { + switch (track->audio.bitdepth) { + case 8: codec_id = AV_CODEC_ID_PCM_U8; break; + case 24: codec_id = AV_CODEC_ID_PCM_S24BE; break; + case 32: codec_id = AV_CODEC_ID_PCM_S32BE; break; + } + } else if (codec_id == AV_CODEC_ID_PCM_S16LE) { + switch (track->audio.bitdepth) { + case 8: codec_id = AV_CODEC_ID_PCM_U8; break; + case 24: codec_id = AV_CODEC_ID_PCM_S24LE; break; + case 32: codec_id = AV_CODEC_ID_PCM_S32LE; break; + } + } else if (codec_id==AV_CODEC_ID_PCM_F32LE && track->audio.bitdepth==64) { + codec_id = AV_CODEC_ID_PCM_F64LE; + } else if (codec_id == AV_CODEC_ID_AAC && !track->codec_priv.size) { + int profile = matroska_aac_profile(track->codec_id); + int sri = matroska_aac_sri(track->audio.samplerate); + extradata = av_mallocz(5 + FF_INPUT_BUFFER_PADDING_SIZE); + if (extradata == NULL) + return AVERROR(ENOMEM); + extradata[0] = (profile << 3) | ((sri&0x0E) >> 1); + extradata[1] = ((sri&0x01) << 7) | (track->audio.channels<<3); + if (strstr(track->codec_id, "SBR")) { + sri = matroska_aac_sri(track->audio.out_samplerate); + extradata[2] = 0x56; + extradata[3] = 0xE5; + extradata[4] = 0x80 | (sri<<3); + extradata_size = 5; + } else + extradata_size = 2; + } else if (codec_id == AV_CODEC_ID_TTA) { + extradata_size = 30; + extradata = av_mallocz(extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (extradata == NULL) + return AVERROR(ENOMEM); + ffio_init_context(&b, extradata, extradata_size, 1, + NULL, NULL, NULL, NULL); + avio_write(&b, "TTA1", 4); + avio_wl16(&b, 1); + avio_wl16(&b, track->audio.channels); + avio_wl16(&b, track->audio.bitdepth); + avio_wl32(&b, track->audio.out_samplerate); + avio_wl32(&b, matroska->ctx->duration * track->audio.out_samplerate); + } else if (codec_id == AV_CODEC_ID_RV10 || codec_id == AV_CODEC_ID_RV20 || + codec_id == AV_CODEC_ID_RV30 || codec_id == AV_CODEC_ID_RV40) { + extradata_offset = 26; + } else if (codec_id == AV_CODEC_ID_RA_144) { + track->audio.out_samplerate = 8000; + track->audio.channels = 1; + } else if ((codec_id == AV_CODEC_ID_RA_288 || codec_id == AV_CODEC_ID_COOK || + codec_id == AV_CODEC_ID_ATRAC3 || codec_id == AV_CODEC_ID_SIPR) + && track->codec_priv.data) { + int flavor; + + ffio_init_context(&b, track->codec_priv.data,track->codec_priv.size, + 0, NULL, NULL, NULL, NULL); + avio_skip(&b, 22); + flavor = avio_rb16(&b); + track->audio.coded_framesize = avio_rb32(&b); + avio_skip(&b, 12); + track->audio.sub_packet_h = avio_rb16(&b); + track->audio.frame_size = avio_rb16(&b); + track->audio.sub_packet_size = avio_rb16(&b); + track->audio.buf = av_malloc(track->audio.frame_size * track->audio.sub_packet_h); + if (codec_id == AV_CODEC_ID_RA_288) { + st->codec->block_align = track->audio.coded_framesize; + track->codec_priv.size = 0; + } else { + if (codec_id == AV_CODEC_ID_SIPR && flavor < 4) { + const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; + track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; + st->codec->bit_rate = sipr_bit_rate[flavor]; + } + st->codec->block_align = track->audio.sub_packet_size; + extradata_offset = 78; + } + } + track->codec_priv.size -= extradata_offset; + + if (codec_id == AV_CODEC_ID_NONE) + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown/unsupported AVCodecID %s.\n", track->codec_id); + + if (track->time_scale < 0.01) + track->time_scale = 1.0; + avpriv_set_pts_info(st, 64, matroska->time_scale*track->time_scale, 1000*1000*1000); /* 64 bit pts in ns */ + + st->codec->codec_id = codec_id; + st->start_time = 0; + if (strcmp(track->language, "und")) + av_dict_set(&st->metadata, "language", track->language, 0); + av_dict_set(&st->metadata, "title", track->name, 0); + + if (track->flag_default) + st->disposition |= AV_DISPOSITION_DEFAULT; + if (track->flag_forced) + st->disposition |= AV_DISPOSITION_FORCED; + + if (!st->codec->extradata) { + if(extradata){ + st->codec->extradata = extradata; + st->codec->extradata_size = extradata_size; + } else if(track->codec_priv.data && track->codec_priv.size > 0){ + st->codec->extradata = av_mallocz(track->codec_priv.size + + FF_INPUT_BUFFER_PADDING_SIZE); + if(st->codec->extradata == NULL) + return AVERROR(ENOMEM); + st->codec->extradata_size = track->codec_priv.size; + memcpy(st->codec->extradata, + track->codec_priv.data + extradata_offset, + track->codec_priv.size); + } + } + + if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { + MatroskaTrackPlane *planes = track->operation.combine_planes.elem; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = fourcc; + st->codec->width = track->video.pixel_width; + st->codec->height = track->video.pixel_height; + av_reduce(&st->sample_aspect_ratio.num, + &st->sample_aspect_ratio.den, + st->codec->height * track->video.display_width, + st->codec-> width * track->video.display_height, + 255); + st->need_parsing = AVSTREAM_PARSE_HEADERS; + if (track->default_duration) { + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + 1000000000, track->default_duration, 30000); +#if FF_API_R_FRAME_RATE + st->r_frame_rate = st->avg_frame_rate; +#endif + } + + /* export stereo mode flag as metadata tag */ + if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREO_MODE_COUNT) + av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0); + + /* export alpha mode flag as metadata tag */ + if (track->video.alpha_mode) + av_dict_set(&st->metadata, "alpha_mode", "1", 0); + + /* if we have virtual track, mark the real tracks */ + for (j=0; j < track->operation.combine_planes.nb_elem; j++) { + char buf[32]; + if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT) + continue; + snprintf(buf, sizeof(buf), "%s_%d", + ff_matroska_video_stereo_plane[planes[j].type], i); + for (k=0; k < matroska->tracks.nb_elem; k++) + if (planes[j].uid == tracks[k].uid) { + av_dict_set(&s->streams[k]->metadata, + "stereo_mode", buf, 0); + break; + } + } + } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->sample_rate = track->audio.out_samplerate; + st->codec->channels = track->audio.channels; + st->codec->bits_per_coded_sample = track->audio.bitdepth; + if (st->codec->codec_id != AV_CODEC_ID_AAC) + st->need_parsing = AVSTREAM_PARSE_HEADERS; + } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; +#if FF_API_ASS_SSA + if (st->codec->codec_id == AV_CODEC_ID_SSA || + st->codec->codec_id == AV_CODEC_ID_ASS) +#else + if (st->codec->codec_id == AV_CODEC_ID_ASS) +#endif + matroska->contains_ssa = 1; + } + } + + attachements = attachements_list->elem; + for (j=0; jnb_elem; j++) { + if (!(attachements[j].filename && attachements[j].mime && + attachements[j].bin.data && attachements[j].bin.size > 0)) { + av_log(matroska->ctx, AV_LOG_ERROR, "incomplete attachment\n"); + } else { + AVStream *st = avformat_new_stream(s, NULL); + if (st == NULL) + break; + av_dict_set(&st->metadata, "filename",attachements[j].filename, 0); + av_dict_set(&st->metadata, "mimetype", attachements[j].mime, 0); + st->codec->codec_id = AV_CODEC_ID_NONE; + st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; + st->codec->extradata = av_malloc(attachements[j].bin.size + FF_INPUT_BUFFER_PADDING_SIZE); + if(st->codec->extradata == NULL) + break; + st->codec->extradata_size = attachements[j].bin.size; + memcpy(st->codec->extradata, attachements[j].bin.data, attachements[j].bin.size); + + for (i=0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { + if (!strncmp(ff_mkv_mime_tags[i].str, attachements[j].mime, + strlen(ff_mkv_mime_tags[i].str))) { + st->codec->codec_id = ff_mkv_mime_tags[i].id; + break; + } + } + attachements[j].stream = st; + } + } + + chapters = chapters_list->elem; + for (i=0; inb_elem; i++) + if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid + && (max_start==0 || chapters[i].start > max_start)) { + chapters[i].chapter = + avpriv_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000}, + chapters[i].start, chapters[i].end, + chapters[i].title); + av_dict_set(&chapters[i].chapter->metadata, + "title", chapters[i].title, 0); + max_start = chapters[i].start; + } + + matroska_add_index_entries(matroska); + + matroska_convert_tags(s); + + return 0; +} + +/* + * Put one packet in an application-supplied AVPacket struct. + * Returns 0 on success or -1 on failure. + */ +static int matroska_deliver_packet(MatroskaDemuxContext *matroska, + AVPacket *pkt) +{ + if (matroska->num_packets > 0) { + memcpy(pkt, matroska->packets[0], sizeof(AVPacket)); + av_free(matroska->packets[0]); + if (matroska->num_packets > 1) { + void *newpackets; + memmove(&matroska->packets[0], &matroska->packets[1], + (matroska->num_packets - 1) * sizeof(AVPacket *)); + newpackets = av_realloc(matroska->packets, + (matroska->num_packets - 1) * sizeof(AVPacket *)); + if (newpackets) + matroska->packets = newpackets; + } else { + av_freep(&matroska->packets); + matroska->prev_pkt = NULL; + } + matroska->num_packets--; + return 0; + } + + return -1; +} + +/* + * Free all packets in our internal queue. + */ +static void matroska_clear_queue(MatroskaDemuxContext *matroska) +{ + matroska->prev_pkt = NULL; + if (matroska->packets) { + int n; + for (n = 0; n < matroska->num_packets; n++) { + av_free_packet(matroska->packets[n]); + av_free(matroska->packets[n]); + } + av_freep(&matroska->packets); + matroska->num_packets = 0; + } +} + +static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, + int* buf_size, int type, + uint32_t **lace_buf, int *laces) +{ + int res = 0, n, size = *buf_size; + uint8_t *data = *buf; + uint32_t *lace_size; + + if (!type) { + *laces = 1; + *lace_buf = av_mallocz(sizeof(int)); + if (!*lace_buf) + return AVERROR(ENOMEM); + + *lace_buf[0] = size; + return 0; + } + + av_assert0(size > 0); + *laces = *data + 1; + data += 1; + size -= 1; + lace_size = av_mallocz(*laces * sizeof(int)); + if (!lace_size) + return AVERROR(ENOMEM); + + switch (type) { + case 0x1: /* Xiph lacing */ { + uint8_t temp; + uint32_t total = 0; + for (n = 0; res == 0 && n < *laces - 1; n++) { + while (1) { + if (size <= total) { + res = AVERROR_INVALIDDATA; + break; + } + temp = *data; + total += temp; + lace_size[n] += temp; + data += 1; + size -= 1; + if (temp != 0xff) + break; + } + } + if (size <= total) { + res = AVERROR_INVALIDDATA; + break; + } + + lace_size[n] = size - total; + break; + } + + case 0x2: /* fixed-size lacing */ + if (size % (*laces)) { + res = AVERROR_INVALIDDATA; + break; + } + for (n = 0; n < *laces; n++) + lace_size[n] = size / *laces; + break; + + case 0x3: /* EBML lacing */ { + uint64_t num; + uint64_t total; + n = matroska_ebmlnum_uint(matroska, data, size, &num); + if (n < 0 || num > INT_MAX) { + av_log(matroska->ctx, AV_LOG_INFO, + "EBML block data error\n"); + res = n<0 ? n : AVERROR_INVALIDDATA; + break; + } + data += n; + size -= n; + total = lace_size[0] = num; + for (n = 1; res == 0 && n < *laces - 1; n++) { + int64_t snum; + int r; + r = matroska_ebmlnum_sint(matroska, data, size, &snum); + if (r < 0 || lace_size[n - 1] + snum > (uint64_t)INT_MAX) { + av_log(matroska->ctx, AV_LOG_INFO, + "EBML block data error\n"); + res = r<0 ? r : AVERROR_INVALIDDATA; + break; + } + data += r; + size -= r; + lace_size[n] = lace_size[n - 1] + snum; + total += lace_size[n]; + } + if (size <= total) { + res = AVERROR_INVALIDDATA; + break; + } + lace_size[*laces - 1] = size - total; + break; + } + } + + *buf = data; + *lace_buf = lace_size; + *buf_size = size; + + return res; +} + +static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, + MatroskaTrack *track, + AVStream *st, + uint8_t *data, int size, + uint64_t timecode, + int64_t pos) +{ + int a = st->codec->block_align; + int sps = track->audio.sub_packet_size; + int cfs = track->audio.coded_framesize; + int h = track->audio.sub_packet_h; + int y = track->audio.sub_packet_cnt; + int w = track->audio.frame_size; + int x; + + if (!track->audio.pkt_cnt) { + if (track->audio.sub_packet_cnt == 0) + track->audio.buf_timecode = timecode; + if (st->codec->codec_id == AV_CODEC_ID_RA_288) { + if (size < cfs * h / 2) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Corrupt int4 RM-style audio packet size\n"); + return AVERROR_INVALIDDATA; + } + for (x=0; xaudio.buf+x*2*w+y*cfs, + data+x*cfs, cfs); + } else if (st->codec->codec_id == AV_CODEC_ID_SIPR) { + if (size < w) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Corrupt sipr RM-style audio packet size\n"); + return AVERROR_INVALIDDATA; + } + memcpy(track->audio.buf + y*w, data, w); + } else { + if (size < sps * w / sps || h<=0) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Corrupt generic RM-style audio packet size\n"); + return AVERROR_INVALIDDATA; + } + for (x=0; xaudio.buf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps); + } + + if (++track->audio.sub_packet_cnt >= h) { + if (st->codec->codec_id == AV_CODEC_ID_SIPR) + ff_rm_reorder_sipr_data(track->audio.buf, h, w); + track->audio.sub_packet_cnt = 0; + track->audio.pkt_cnt = h*w / a; + } + } + + while (track->audio.pkt_cnt) { + AVPacket *pkt = NULL; + if (!(pkt = av_mallocz(sizeof(AVPacket))) || av_new_packet(pkt, a) < 0){ + av_free(pkt); + return AVERROR(ENOMEM); + } + memcpy(pkt->data, track->audio.buf + + a * (h*w / a - track->audio.pkt_cnt--), a); + pkt->pts = track->audio.buf_timecode; + track->audio.buf_timecode = AV_NOPTS_VALUE; + pkt->pos = pos; + pkt->stream_index = st->index; + dynarray_add(&matroska->packets,&matroska->num_packets,pkt); + } + + return 0; +} +static int matroska_parse_frame(MatroskaDemuxContext *matroska, + MatroskaTrack *track, + AVStream *st, + uint8_t *data, int pkt_size, + uint64_t timecode, uint64_t lace_duration, + int64_t pos, int is_keyframe, + uint8_t *additional, uint64_t additional_id, int additional_size) +{ + MatroskaTrackEncoding *encodings = track->encodings.elem; + uint8_t *pkt_data = data; + int offset = 0, res; + AVPacket *pkt; + + if (encodings && !encodings->type && encodings->scope & 1) { + res = matroska_decode_buffer(&pkt_data, &pkt_size, track); + if (res < 0) + return res; + } + + if (st->codec->codec_id == AV_CODEC_ID_PRORES) + offset = 8; + + pkt = av_mallocz(sizeof(AVPacket)); + /* XXX: prevent data copy... */ + if (av_new_packet(pkt, pkt_size + offset) < 0) { + av_free(pkt); + return AVERROR(ENOMEM); + } + + if (st->codec->codec_id == AV_CODEC_ID_PRORES) { + uint8_t *buf = pkt->data; + bytestream_put_be32(&buf, pkt_size); + bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f')); + } + + memcpy(pkt->data + offset, pkt_data, pkt_size); + + if (pkt_data != data) + av_free(pkt_data); + + pkt->flags = is_keyframe; + pkt->stream_index = st->index; + + if (additional_size > 0) { + uint8_t *side_data = av_packet_new_side_data(pkt, + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + additional_size + 8); + if(side_data == NULL) { + av_free_packet(pkt); + av_free(pkt); + return AVERROR(ENOMEM); + } + AV_WB64(side_data, additional_id); + memcpy(side_data + 8, additional, additional_size); + } + + if (track->ms_compat) + pkt->dts = timecode; + else + pkt->pts = timecode; + pkt->pos = pos; + if (st->codec->codec_id == AV_CODEC_ID_SUBRIP) { + /* + * For backward compatibility. + * Historically, we have put subtitle duration + * in convergence_duration, on the off chance + * that the time_scale is less than 1us, which + * could result in a 32bit overflow on the + * normal duration field. + */ + pkt->convergence_duration = lace_duration; + } + + if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE || + lace_duration <= INT_MAX) { + /* + * For non subtitle tracks, just store the duration + * as normal. + * + * If it's a subtitle track and duration value does + * not overflow a uint32, then also store it normally. + */ + pkt->duration = lace_duration; + } + +#if FF_API_ASS_SSA + if (st->codec->codec_id == AV_CODEC_ID_SSA) + matroska_fix_ass_packet(matroska, pkt, lace_duration); + + if (matroska->prev_pkt && + timecode != AV_NOPTS_VALUE && + matroska->prev_pkt->pts == timecode && + matroska->prev_pkt->stream_index == st->index && + st->codec->codec_id == AV_CODEC_ID_SSA) + matroska_merge_packets(matroska->prev_pkt, pkt); + else { + dynarray_add(&matroska->packets,&matroska->num_packets,pkt); + matroska->prev_pkt = pkt; + } +#else + dynarray_add(&matroska->packets, &matroska->num_packets, pkt); + matroska->prev_pkt = pkt; +#endif + + return 0; +} + +static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, + int size, int64_t pos, uint64_t cluster_time, + uint64_t block_duration, int is_keyframe, + uint8_t *additional, uint64_t additional_id, int additional_size, + int64_t cluster_pos) +{ + uint64_t timecode = AV_NOPTS_VALUE; + MatroskaTrack *track; + int res = 0; + AVStream *st; + int16_t block_time; + uint32_t *lace_size = NULL; + int n, flags, laces = 0; + uint64_t num; + + if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { + av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n"); + return n; + } + data += n; + size -= n; + + track = matroska_find_track_by_num(matroska, num); + if (!track || !track->stream) { + av_log(matroska->ctx, AV_LOG_INFO, + "Invalid stream %"PRIu64" or size %u\n", num, size); + return AVERROR_INVALIDDATA; + } else if (size <= 3) + return 0; + st = track->stream; + if (st->discard >= AVDISCARD_ALL) + return res; + av_assert1(block_duration != AV_NOPTS_VALUE); + + block_time = AV_RB16(data); + data += 2; + flags = *data++; + size -= 3; + if (is_keyframe == -1) + is_keyframe = flags & 0x80 ? AV_PKT_FLAG_KEY : 0; + + if (cluster_time != (uint64_t)-1 + && (block_time >= 0 || cluster_time >= -block_time)) { + timecode = cluster_time + block_time; + if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE + && timecode < track->end_timecode) + is_keyframe = 0; /* overlapping subtitles are not key frame */ + if (is_keyframe) + av_add_index_entry(st, cluster_pos, timecode, 0,0,AVINDEX_KEYFRAME); + } + + if (matroska->skip_to_keyframe && track->type != MATROSKA_TRACK_TYPE_SUBTITLE) { + if (timecode < matroska->skip_to_timecode) + return res; + if (!st->skip_to_keyframe) { + av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n"); + matroska->skip_to_keyframe = 0; + } + if (is_keyframe) + matroska->skip_to_keyframe = 0; + } + + res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1, + &lace_size, &laces); + + if (res) + goto end; + + if (!block_duration) + block_duration = track->default_duration * laces / matroska->time_scale; + + if (cluster_time != (uint64_t)-1 && (block_time >= 0 || cluster_time >= -block_time)) + track->end_timecode = + FFMAX(track->end_timecode, timecode + block_duration); + + for (n = 0; n < laces; n++) { + int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces; + + if (lace_size[n] > size) { + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n"); + break; + } + + if ((st->codec->codec_id == AV_CODEC_ID_RA_288 || + st->codec->codec_id == AV_CODEC_ID_COOK || + st->codec->codec_id == AV_CODEC_ID_SIPR || + st->codec->codec_id == AV_CODEC_ID_ATRAC3) && + st->codec->block_align && track->audio.sub_packet_size) { + + res = matroska_parse_rm_audio(matroska, track, st, data, + lace_size[n], + timecode, pos); + if (res) + goto end; + + } else { + res = matroska_parse_frame(matroska, track, st, data, lace_size[n], + timecode, lace_duration, + pos, !n? is_keyframe : 0, + additional, additional_id, additional_size); + if (res) + goto end; + } + + if (timecode != AV_NOPTS_VALUE) + timecode = lace_duration ? timecode + lace_duration : AV_NOPTS_VALUE; + data += lace_size[n]; + size -= lace_size[n]; + } + +end: + av_free(lace_size); + return res; +} + +static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska) +{ + EbmlList *blocks_list; + MatroskaBlock *blocks; + int i, res; + res = ebml_parse(matroska, + matroska_cluster_incremental_parsing, + &matroska->current_cluster); + if (res == 1) { + /* New Cluster */ + if (matroska->current_cluster_pos) + ebml_level_end(matroska); + ebml_free(matroska_cluster, &matroska->current_cluster); + memset(&matroska->current_cluster, 0, sizeof(MatroskaCluster)); + matroska->current_cluster_num_blocks = 0; + matroska->current_cluster_pos = avio_tell(matroska->ctx->pb); + matroska->prev_pkt = NULL; + /* sizeof the ID which was already read */ + if (matroska->current_id) + matroska->current_cluster_pos -= 4; + res = ebml_parse(matroska, + matroska_clusters_incremental, + &matroska->current_cluster); + /* Try parsing the block again. */ + if (res == 1) + res = ebml_parse(matroska, + matroska_cluster_incremental_parsing, + &matroska->current_cluster); + } + + if (!res && + matroska->current_cluster_num_blocks < + matroska->current_cluster.blocks.nb_elem) { + blocks_list = &matroska->current_cluster.blocks; + blocks = blocks_list->elem; + + matroska->current_cluster_num_blocks = blocks_list->nb_elem; + i = blocks_list->nb_elem - 1; + if (blocks[i].bin.size > 0 && blocks[i].bin.data) { + int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1; + uint8_t* additional = blocks[i].additional.size > 0 ? + blocks[i].additional.data : NULL; + if (!blocks[i].non_simple) + blocks[i].duration = 0; + res = matroska_parse_block(matroska, + blocks[i].bin.data, blocks[i].bin.size, + blocks[i].bin.pos, + matroska->current_cluster.timecode, + blocks[i].duration, is_keyframe, + additional, blocks[i].additional_id, + blocks[i].additional.size, + matroska->current_cluster_pos); + } + } + + if (res < 0) matroska->done = 1; + return res; +} + +static int matroska_parse_cluster(MatroskaDemuxContext *matroska) +{ + MatroskaCluster cluster = { 0 }; + EbmlList *blocks_list; + MatroskaBlock *blocks; + int i, res; + int64_t pos; + if (!matroska->contains_ssa) + return matroska_parse_cluster_incremental(matroska); + pos = avio_tell(matroska->ctx->pb); + matroska->prev_pkt = NULL; + if (matroska->current_id) + pos -= 4; /* sizeof the ID which was already read */ + res = ebml_parse(matroska, matroska_clusters, &cluster); + blocks_list = &cluster.blocks; + blocks = blocks_list->elem; + for (i=0; inb_elem; i++) + if (blocks[i].bin.size > 0 && blocks[i].bin.data) { + int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1; + res=matroska_parse_block(matroska, + blocks[i].bin.data, blocks[i].bin.size, + blocks[i].bin.pos, cluster.timecode, + blocks[i].duration, is_keyframe, NULL, 0, 0, + pos); + } + ebml_free(matroska_cluster, &cluster); + return res; +} + +static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaDemuxContext *matroska = s->priv_data; + + while (matroska_deliver_packet(matroska, pkt)) { + int64_t pos = avio_tell(matroska->ctx->pb); + if (matroska->done) + return AVERROR_EOF; + if (matroska_parse_cluster(matroska) < 0) + matroska_resync(matroska, pos); + } + + return 0; +} + +static int matroska_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + MatroskaDemuxContext *matroska = s->priv_data; + MatroskaTrack *tracks = matroska->tracks.elem; + AVStream *st = s->streams[stream_index]; + int i, index, index_sub, index_min; + + /* Parse the CUES now since we need the index data to seek. */ + if (matroska->cues_parsing_deferred > 0) { + matroska->cues_parsing_deferred = 0; + matroska_parse_cues(matroska); + } + + if (!st->nb_index_entries) + goto err; + timestamp = FFMAX(timestamp, st->index_entries[0].timestamp); + + if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) { + avio_seek(s->pb, st->index_entries[st->nb_index_entries-1].pos, SEEK_SET); + matroska->current_id = 0; + while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) { + matroska_clear_queue(matroska); + if (matroska_parse_cluster(matroska) < 0) + break; + } + } + + matroska_clear_queue(matroska); + if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1)) + goto err; + + index_min = index; + for (i=0; i < matroska->tracks.nb_elem; i++) { + tracks[i].audio.pkt_cnt = 0; + tracks[i].audio.sub_packet_cnt = 0; + tracks[i].audio.buf_timecode = AV_NOPTS_VALUE; + tracks[i].end_timecode = 0; + if (tracks[i].type == MATROSKA_TRACK_TYPE_SUBTITLE + && tracks[i].stream->discard != AVDISCARD_ALL) { + index_sub = av_index_search_timestamp(tracks[i].stream, st->index_entries[index].timestamp, AVSEEK_FLAG_BACKWARD); + if (index_sub >= 0 + && st->index_entries[index_sub].pos < st->index_entries[index_min].pos + && st->index_entries[index].timestamp - st->index_entries[index_sub].timestamp < 30000000000/matroska->time_scale) + index_min = index_sub; + } + } + + avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET); + matroska->current_id = 0; + if (flags & AVSEEK_FLAG_ANY) { + st->skip_to_keyframe = 0; + matroska->skip_to_timecode = timestamp; + } else { + st->skip_to_keyframe = 1; + matroska->skip_to_timecode = st->index_entries[index].timestamp; + } + matroska->skip_to_keyframe = 1; + matroska->done = 0; + matroska->num_levels = 0; + ff_update_cur_dts(s, st, st->index_entries[index].timestamp); + return 0; +err: + // slightly hackish but allows proper fallback to + // the generic seeking code. + matroska_clear_queue(matroska); + matroska->current_id = 0; + st->skip_to_keyframe = + matroska->skip_to_keyframe = 0; + matroska->done = 0; + matroska->num_levels = 0; + return -1; +} + +static int matroska_read_close(AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + MatroskaTrack *tracks = matroska->tracks.elem; + int n; + + matroska_clear_queue(matroska); + + for (n=0; n < matroska->tracks.nb_elem; n++) + if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO) + av_free(tracks[n].audio.buf); + ebml_free(matroska_cluster, &matroska->current_cluster); + ebml_free(matroska_segment, matroska); + + return 0; +} + +AVInputFormat ff_matroska_demuxer = { + .name = "matroska,webm", + .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), + .priv_data_size = sizeof(MatroskaDemuxContext), + .read_probe = matroska_probe, + .read_header = matroska_read_header, + .read_packet = matroska_read_packet, + .read_close = matroska_read_close, + .read_seek = matroska_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskadec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroskadec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,28 @@ +libavformat/matroskadec.o libavformat/matroskadec.o: \ + libavformat/matroskadec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/riff.h \ + libavformat/metadata.h libavformat/isom.h libavformat/dv.h \ + libavformat/rmsipr.h libavformat/matroska.h libavcodec/bytestream.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavcodec/mpeg4audio.h \ + libavcodec/get_bits.h libavutil/avassert.h libavcodec/mathops.h \ + config.h libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/put_bits.h libavutil/bswap.h libavutil/base64.h \ + libavutil/intfloat.h libavutil/intreadwrite.h libavutil/avstring.h \ + libavutil/lzo.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskadec.o Binary file ffmpeg/libavformat/matroskadec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskaenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroskaenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1469 @@ +/* + * Matroska muxer + * Copyright (c) 2007 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include "isom.h" +#include "matroska.h" +#include "avc.h" +#include "flacenc.h" +#include "avlanguage.h" +#include "libavutil/samplefmt.h" +#include "libavutil/sha.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/mathematics.h" +#include "libavutil/random_seed.h" +#include "libavutil/lfg.h" +#include "libavutil/dict.h" +#include "libavutil/avstring.h" +#include "libavcodec/xiph.h" +#include "libavcodec/mpeg4audio.h" + +typedef struct ebml_master { + int64_t pos; ///< absolute offset in the file where the master's elements start + int sizebytes; ///< how many bytes were reserved for the size +} ebml_master; + +typedef struct mkv_seekhead_entry { + unsigned int elementid; + uint64_t segmentpos; +} mkv_seekhead_entry; + +typedef struct mkv_seekhead { + int64_t filepos; + int64_t segment_offset; ///< the file offset to the beginning of the segment + int reserved_size; ///< -1 if appending to file + int max_entries; + mkv_seekhead_entry *entries; + int num_entries; +} mkv_seekhead; + +typedef struct { + uint64_t pts; + int tracknum; + int64_t cluster_pos; ///< file offset of the cluster containing the block +} mkv_cuepoint; + +typedef struct { + int64_t segment_offset; + mkv_cuepoint *entries; + int num_entries; +} mkv_cues; + +typedef struct { + int write_dts; + int has_cue; +} mkv_track; + +#define MODE_MATROSKAv2 0x01 +#define MODE_WEBM 0x02 + +typedef struct MatroskaMuxContext { + int mode; + AVIOContext *dyn_bc; + ebml_master segment; + int64_t segment_offset; + ebml_master cluster; + int64_t cluster_pos; ///< file offset of the current cluster + int64_t cluster_pts; + int64_t duration_offset; + int64_t duration; + mkv_seekhead *main_seekhead; + mkv_cues *cues; + mkv_track *tracks; + + AVPacket cur_audio_pkt; + + int have_attachments; +} MatroskaMuxContext; + + +/** 2 bytes * 3 for EBML IDs, 3 1-byte EBML lengths, 8 bytes for 64 bit + * offset, 4 bytes for target EBML ID */ +#define MAX_SEEKENTRY_SIZE 21 + +/** per-cuepoint-track - 3 1-byte EBML IDs, 3 1-byte EBML sizes, 2 + * 8-byte uint max */ +#define MAX_CUETRACKPOS_SIZE 22 + +/** per-cuepoint - 2 1-byte EBML IDs, 2 1-byte EBML sizes, 8-byte uint max */ +#define MAX_CUEPOINT_SIZE(num_tracks) 12 + MAX_CUETRACKPOS_SIZE*num_tracks + + +static int ebml_id_size(unsigned int id) +{ + return (av_log2(id+1)-1)/7+1; +} + +static void put_ebml_id(AVIOContext *pb, unsigned int id) +{ + int i = ebml_id_size(id); + while (i--) + avio_w8(pb, (uint8_t)(id >> (i*8))); +} + +/** + * Write an EBML size meaning "unknown size". + * + * @param bytes The number of bytes the size should occupy (maximum: 8). + */ +static void put_ebml_size_unknown(AVIOContext *pb, int bytes) +{ + av_assert0(bytes <= 8); + avio_w8(pb, 0x1ff >> bytes); + while (--bytes) + avio_w8(pb, 0xff); +} + +/** + * Calculate how many bytes are needed to represent a given number in EBML. + */ +static int ebml_num_size(uint64_t num) +{ + int bytes = 1; + while ((num+1) >> bytes*7) bytes++; + return bytes; +} + +/** + * Write a number in EBML variable length format. + * + * @param bytes The number of bytes that need to be used to write the number. + * If zero, any number of bytes can be used. + */ +static void put_ebml_num(AVIOContext *pb, uint64_t num, int bytes) +{ + int i, needed_bytes = ebml_num_size(num); + + // sizes larger than this are currently undefined in EBML + av_assert0(num < (1ULL<<56)-1); + + if (bytes == 0) + // don't care how many bytes are used, so use the min + bytes = needed_bytes; + // the bytes needed to write the given size would exceed the bytes + // that we need to use, so write unknown size. This shouldn't happen. + av_assert0(bytes >= needed_bytes); + + num |= 1ULL << bytes*7; + for (i = bytes - 1; i >= 0; i--) + avio_w8(pb, (uint8_t)(num >> i*8)); +} + +static void put_ebml_uint(AVIOContext *pb, unsigned int elementid, uint64_t val) +{ + int i, bytes = 1; + uint64_t tmp = val; + while (tmp>>=8) bytes++; + + put_ebml_id(pb, elementid); + put_ebml_num(pb, bytes, 0); + for (i = bytes - 1; i >= 0; i--) + avio_w8(pb, (uint8_t)(val >> i*8)); +} + +static void put_ebml_float(AVIOContext *pb, unsigned int elementid, double val) +{ + put_ebml_id(pb, elementid); + put_ebml_num(pb, 8, 0); + avio_wb64(pb, av_double2int(val)); +} + +static void put_ebml_binary(AVIOContext *pb, unsigned int elementid, + const void *buf, int size) +{ + put_ebml_id(pb, elementid); + put_ebml_num(pb, size, 0); + avio_write(pb, buf, size); +} + +static void put_ebml_string(AVIOContext *pb, unsigned int elementid, const char *str) +{ + put_ebml_binary(pb, elementid, str, strlen(str)); +} + +/** + * Write a void element of a given size. Useful for reserving space in + * the file to be written to later. + * + * @param size The number of bytes to reserve, which must be at least 2. + */ +static void put_ebml_void(AVIOContext *pb, uint64_t size) +{ + int64_t currentpos = avio_tell(pb); + + av_assert0(size >= 2); + + put_ebml_id(pb, EBML_ID_VOID); + // we need to subtract the length needed to store the size from the + // size we need to reserve so 2 cases, we use 8 bytes to store the + // size if possible, 1 byte otherwise + if (size < 10) + put_ebml_num(pb, size-1, 0); + else + put_ebml_num(pb, size-9, 8); + while(avio_tell(pb) < currentpos + size) + avio_w8(pb, 0); +} + +static ebml_master start_ebml_master(AVIOContext *pb, unsigned int elementid, uint64_t expectedsize) +{ + int bytes = expectedsize ? ebml_num_size(expectedsize) : 8; + put_ebml_id(pb, elementid); + put_ebml_size_unknown(pb, bytes); + return (ebml_master){ avio_tell(pb), bytes }; +} + +static void end_ebml_master(AVIOContext *pb, ebml_master master) +{ + int64_t pos = avio_tell(pb); + + if (avio_seek(pb, master.pos - master.sizebytes, SEEK_SET) < 0) + return; + put_ebml_num(pb, pos - master.pos, master.sizebytes); + avio_seek(pb, pos, SEEK_SET); +} + +static void put_xiph_size(AVIOContext *pb, int size) +{ + int i; + for (i = 0; i < size / 255; i++) + avio_w8(pb, 255); + avio_w8(pb, size % 255); +} + +/** + * Initialize a mkv_seekhead element to be ready to index level 1 Matroska + * elements. If a maximum number of elements is specified, enough space + * will be reserved at the current file location to write a seek head of + * that size. + * + * @param segment_offset The absolute offset to the position in the file + * where the segment begins. + * @param numelements The maximum number of elements that will be indexed + * by this seek head, 0 if unlimited. + */ +static mkv_seekhead * mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset, int numelements) +{ + mkv_seekhead *new_seekhead = av_mallocz(sizeof(mkv_seekhead)); + if (new_seekhead == NULL) + return NULL; + + new_seekhead->segment_offset = segment_offset; + + if (numelements > 0) { + new_seekhead->filepos = avio_tell(pb); + // 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID + // and size, and 3 bytes to guarantee that an EBML void element + // will fit afterwards + new_seekhead->reserved_size = numelements * MAX_SEEKENTRY_SIZE + 13; + new_seekhead->max_entries = numelements; + put_ebml_void(pb, new_seekhead->reserved_size); + } + return new_seekhead; +} + +static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid, uint64_t filepos) +{ + mkv_seekhead_entry *entries = seekhead->entries; + + // don't store more elements than we reserved space for + if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries) + return -1; + + entries = av_realloc(entries, (seekhead->num_entries + 1) * sizeof(mkv_seekhead_entry)); + if (entries == NULL) + return AVERROR(ENOMEM); + + entries[seekhead->num_entries ].elementid = elementid; + entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset; + + seekhead->entries = entries; + return 0; +} + +/** + * Write the seek head to the file and free it. If a maximum number of + * elements was specified to mkv_start_seekhead(), the seek head will + * be written at the location reserved for it. Otherwise, it is written + * at the current location in the file. + * + * @return The file offset where the seekhead was written, + * -1 if an error occurred. + */ +static int64_t mkv_write_seekhead(AVIOContext *pb, mkv_seekhead *seekhead) +{ + ebml_master metaseek, seekentry; + int64_t currentpos; + int i; + + currentpos = avio_tell(pb); + + if (seekhead->reserved_size > 0) { + if (avio_seek(pb, seekhead->filepos, SEEK_SET) < 0) { + currentpos = -1; + goto fail; + } + } + + metaseek = start_ebml_master(pb, MATROSKA_ID_SEEKHEAD, seekhead->reserved_size); + for (i = 0; i < seekhead->num_entries; i++) { + mkv_seekhead_entry *entry = &seekhead->entries[i]; + + seekentry = start_ebml_master(pb, MATROSKA_ID_SEEKENTRY, MAX_SEEKENTRY_SIZE); + + put_ebml_id(pb, MATROSKA_ID_SEEKID); + put_ebml_num(pb, ebml_id_size(entry->elementid), 0); + put_ebml_id(pb, entry->elementid); + + put_ebml_uint(pb, MATROSKA_ID_SEEKPOSITION, entry->segmentpos); + end_ebml_master(pb, seekentry); + } + end_ebml_master(pb, metaseek); + + if (seekhead->reserved_size > 0) { + uint64_t remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb); + put_ebml_void(pb, remaining); + avio_seek(pb, currentpos, SEEK_SET); + + currentpos = seekhead->filepos; + } +fail: + av_free(seekhead->entries); + av_free(seekhead); + + return currentpos; +} + +static mkv_cues * mkv_start_cues(int64_t segment_offset) +{ + mkv_cues *cues = av_mallocz(sizeof(mkv_cues)); + if (cues == NULL) + return NULL; + + cues->segment_offset = segment_offset; + return cues; +} + +static int mkv_add_cuepoint(mkv_cues *cues, int stream, int64_t ts, int64_t cluster_pos) +{ + mkv_cuepoint *entries = cues->entries; + + if (ts < 0) + return 0; + + entries = av_realloc(entries, (cues->num_entries + 1) * sizeof(mkv_cuepoint)); + if (entries == NULL) + return AVERROR(ENOMEM); + + entries[cues->num_entries ].pts = ts; + entries[cues->num_entries ].tracknum = stream + 1; + entries[cues->num_entries++].cluster_pos = cluster_pos - cues->segment_offset; + + cues->entries = entries; + return 0; +} + +static int64_t mkv_write_cues(AVIOContext *pb, mkv_cues *cues, mkv_track *tracks, int num_tracks) +{ + ebml_master cues_element; + int64_t currentpos; + int i, j; + + currentpos = avio_tell(pb); + cues_element = start_ebml_master(pb, MATROSKA_ID_CUES, 0); + + for (i = 0; i < cues->num_entries; i++) { + ebml_master cuepoint, track_positions; + mkv_cuepoint *entry = &cues->entries[i]; + uint64_t pts = entry->pts; + + cuepoint = start_ebml_master(pb, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_SIZE(num_tracks)); + put_ebml_uint(pb, MATROSKA_ID_CUETIME, pts); + + // put all the entries from different tracks that have the exact same + // timestamp into the same CuePoint + for (j = 0; j < num_tracks; j++) + tracks[j].has_cue = 0; + for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) { + int tracknum = entry[j].tracknum - 1; + av_assert0(tracknum>=0 && tracknumcodec_id == AV_CODEC_ID_VORBIS) + first_header_size = 30; + else + first_header_size = 42; + + if (avpriv_split_xiph_headers(codec->extradata, codec->extradata_size, + first_header_size, header_start, header_len) < 0) { + av_log(s, AV_LOG_ERROR, "Extradata corrupt.\n"); + return -1; + } + + avio_w8(pb, 2); // number packets - 1 + for (j = 0; j < 2; j++) { + put_xiph_size(pb, header_len[j]); + } + for (j = 0; j < 3; j++) + avio_write(pb, header_start[j], header_len[j]); + + return 0; +} + +static void get_aac_sample_rates(AVFormatContext *s, AVCodecContext *codec, int *sample_rate, int *output_sample_rate) +{ + MPEG4AudioConfig mp4ac; + + if (avpriv_mpeg4audio_get_config(&mp4ac, codec->extradata, + codec->extradata_size * 8, 1) < 0) { + av_log(s, AV_LOG_WARNING, "Error parsing AAC extradata, unable to determine samplerate.\n"); + return; + } + + *sample_rate = mp4ac.sample_rate; + *output_sample_rate = mp4ac.ext_sample_rate; +} + +static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, AVCodecContext *codec, int native_id, int qt_id) +{ + AVIOContext *dyn_cp; + uint8_t *codecpriv; + int ret, codecpriv_size; + + ret = avio_open_dyn_buf(&dyn_cp); + if(ret < 0) + return ret; + + if (native_id) { + if (codec->codec_id == AV_CODEC_ID_VORBIS || codec->codec_id == AV_CODEC_ID_THEORA) + ret = put_xiph_codecpriv(s, dyn_cp, codec); + else if (codec->codec_id == AV_CODEC_ID_FLAC) + ret = ff_flac_write_header(dyn_cp, codec, 1); + else if (codec->codec_id == AV_CODEC_ID_H264) + ret = ff_isom_write_avcc(dyn_cp, codec->extradata, codec->extradata_size); + else if (codec->codec_id == AV_CODEC_ID_ALAC) { + if (codec->extradata_size < 36) { + av_log(s, AV_LOG_ERROR, + "Invalid extradata found, ALAC expects a 36-byte " + "QuickTime atom."); + ret = AVERROR_INVALIDDATA; + } else + avio_write(dyn_cp, codec->extradata + 12, + codec->extradata_size - 12); + } + else if (codec->extradata_size && codec->codec_id != AV_CODEC_ID_TTA) + avio_write(dyn_cp, codec->extradata, codec->extradata_size); + } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (qt_id) { + if (!codec->codec_tag) + codec->codec_tag = ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id); + if (codec->extradata_size) + avio_write(dyn_cp, codec->extradata, codec->extradata_size); + } else { + if (!codec->codec_tag) + codec->codec_tag = ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id); + if (!codec->codec_tag) { + av_log(s, AV_LOG_ERROR, "No bmp codec tag found for codec %s\n", + avcodec_get_name(codec->codec_id)); + ret = AVERROR(EINVAL); + } + + ff_put_bmp_header(dyn_cp, codec, ff_codec_bmp_tags, 0); + } + + } else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + unsigned int tag; + tag = ff_codec_get_tag(ff_codec_wav_tags, codec->codec_id); + if (!tag) { + av_log(s, AV_LOG_ERROR, "No wav codec tag found for codec %s\n", + avcodec_get_name(codec->codec_id)); + ret = AVERROR(EINVAL); + } + if (!codec->codec_tag) + codec->codec_tag = tag; + + ff_put_wav_header(dyn_cp, codec); + } + + codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv); + if (codecpriv_size) + put_ebml_binary(pb, MATROSKA_ID_CODECPRIVATE, codecpriv, codecpriv_size); + av_free(codecpriv); + return ret; +} + +static int mkv_write_tracks(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + ebml_master tracks; + int i, j, ret, default_stream_exists = 0; + + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); + if (ret < 0) return ret; + + tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS, 0); + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT; + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVCodecContext *codec = st->codec; + ebml_master subinfo, track; + int native_id = 0; + int qt_id = 0; + int bit_depth = av_get_bits_per_sample(codec->codec_id); + int sample_rate = codec->sample_rate; + int output_sample_rate = 0; + AVDictionaryEntry *tag; + + if (codec->codec_type == AVMEDIA_TYPE_ATTACHMENT) { + mkv->have_attachments = 1; + continue; + } + + if (!bit_depth) + bit_depth = av_get_bytes_per_sample(codec->sample_fmt) << 3; + if (!bit_depth) + bit_depth = codec->bits_per_coded_sample; + + if (codec->codec_id == AV_CODEC_ID_AAC) + get_aac_sample_rates(s, codec, &sample_rate, &output_sample_rate); + + track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); + put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER , i + 1); + put_ebml_uint (pb, MATROSKA_ID_TRACKUID , i + 1); + put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet) + + if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) + put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value); + tag = av_dict_get(st->metadata, "language", NULL, 0); + put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag ? tag->value:"und"); + + if (default_stream_exists) { + put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT)); + } + if (st->disposition & AV_DISPOSITION_FORCED) + put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, 1); + + // look for a codec ID string specific to mkv to use, + // if none are found, use AVI codes + for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { + if (ff_mkv_codec_tags[j].id == codec->codec_id) { + put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); + native_id = 1; + break; + } + } + + if (mkv->mode == MODE_WEBM && !(codec->codec_id == AV_CODEC_ID_VP8 || + codec->codec_id == AV_CODEC_ID_VORBIS)) { + av_log(s, AV_LOG_ERROR, + "Only VP8 video and Vorbis audio are supported for WebM.\n"); + return AVERROR(EINVAL); + } + + switch (codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); + if(st->avg_frame_rate.num && st->avg_frame_rate.den && 1.0/av_q2d(st->avg_frame_rate) > av_q2d(codec->time_base)) + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1E9/av_q2d(st->avg_frame_rate)); + else + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, av_q2d(codec->time_base)*1E9); + + if (!native_id && + ff_codec_get_tag(ff_codec_movvideo_tags, codec->codec_id) && + (!ff_codec_get_tag(ff_codec_bmp_tags, codec->codec_id) + || codec->codec_id == AV_CODEC_ID_SVQ1 + || codec->codec_id == AV_CODEC_ID_SVQ3 + || codec->codec_id == AV_CODEC_ID_CINEPAK)) + qt_id = 1; + + if (qt_id) + put_ebml_string(pb, MATROSKA_ID_CODECID, "V_QUICKTIME"); + else if (!native_id) { + // if there is no mkv-specific codec ID, use VFW mode + put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC"); + mkv->tracks[i].write_dts = 1; + } + + subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKVIDEO, 0); + // XXX: interlace flag? + put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELWIDTH , codec->width); + put_ebml_uint (pb, MATROSKA_ID_VIDEOPIXELHEIGHT, codec->height); + + if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { + // save stereo mode flag + uint64_t st_mode = MATROSKA_VIDEO_STEREO_MODE_COUNT; + + for (j=0; jvalue, ff_matroska_video_stereo_mode[j])){ + st_mode = j; + break; + } + + if ((mkv->mode == MODE_WEBM && st_mode > 3 && st_mode != 11) + || st_mode >= MATROSKA_VIDEO_STEREO_MODE_COUNT) { + av_log(s, AV_LOG_ERROR, + "The specified stereo mode is not valid.\n"); + return AVERROR(EINVAL); + } else + put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, st_mode); + } + + if (st->sample_aspect_ratio.num) { + int64_t d_width = av_rescale(codec->width, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); + if (d_width > INT_MAX) { + av_log(s, AV_LOG_ERROR, "Overflow in display width\n"); + return AVERROR(EINVAL); + } + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYWIDTH , d_width); + put_ebml_uint(pb, MATROSKA_ID_VIDEODISPLAYHEIGHT, codec->height); + } + + if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) { + uint32_t color_space = av_le2ne32(codec->codec_tag); + put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space)); + } + end_ebml_master(pb, subinfo); + break; + + case AVMEDIA_TYPE_AUDIO: + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO); + + if (!native_id) + // no mkv-specific ID, use ACM mode + put_ebml_string(pb, MATROSKA_ID_CODECID, "A_MS/ACM"); + + subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0); + put_ebml_uint (pb, MATROSKA_ID_AUDIOCHANNELS , codec->channels); + put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate); + if (output_sample_rate) + put_ebml_float(pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); + if (bit_depth) + put_ebml_uint(pb, MATROSKA_ID_AUDIOBITDEPTH, bit_depth); + end_ebml_master(pb, subinfo); + break; + + case AVMEDIA_TYPE_SUBTITLE: + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_SUBTITLE); + if (!native_id) { + av_log(s, AV_LOG_ERROR, "Subtitle codec %d is not supported.\n", codec->codec_id); + return AVERROR(ENOSYS); + } + break; + default: + av_log(s, AV_LOG_ERROR, "Only audio, video, and subtitles are supported for Matroska.\n"); + break; + } + ret = mkv_write_codecprivate(s, pb, codec, native_id, qt_id); + if (ret < 0) return ret; + + end_ebml_master(pb, track); + + // ms precision is the de-facto standard timescale for mkv files + avpriv_set_pts_info(st, 64, 1, 1000); + } + end_ebml_master(pb, tracks); + return 0; +} + +static int mkv_write_chapters(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + ebml_master chapters, editionentry; + AVRational scale = {1, 1E9}; + int i, ret; + + if (!s->nb_chapters) + return 0; + + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CHAPTERS, avio_tell(pb)); + if (ret < 0) return ret; + + chapters = start_ebml_master(pb, MATROSKA_ID_CHAPTERS , 0); + editionentry = start_ebml_master(pb, MATROSKA_ID_EDITIONENTRY, 0); + put_ebml_uint(pb, MATROSKA_ID_EDITIONFLAGDEFAULT, 1); + put_ebml_uint(pb, MATROSKA_ID_EDITIONFLAGHIDDEN , 0); + for (i = 0; i < s->nb_chapters; i++) { + ebml_master chapteratom, chapterdisplay; + AVChapter *c = s->chapters[i]; + AVDictionaryEntry *t = NULL; + + chapteratom = start_ebml_master(pb, MATROSKA_ID_CHAPTERATOM, 0); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERUID, c->id); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMESTART, + av_rescale_q(c->start, c->time_base, scale)); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERTIMEEND, + av_rescale_q(c->end, c->time_base, scale)); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0); + put_ebml_uint(pb, MATROSKA_ID_CHAPTERFLAGENABLED, 1); + if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { + chapterdisplay = start_ebml_master(pb, MATROSKA_ID_CHAPTERDISPLAY, 0); + put_ebml_string(pb, MATROSKA_ID_CHAPSTRING, t->value); + put_ebml_string(pb, MATROSKA_ID_CHAPLANG , "und"); + end_ebml_master(pb, chapterdisplay); + } + end_ebml_master(pb, chapteratom); + } + end_ebml_master(pb, editionentry); + end_ebml_master(pb, chapters); + return 0; +} + +static void mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t) +{ + uint8_t *key = av_strdup(t->key); + uint8_t *p = key; + const uint8_t *lang = NULL; + ebml_master tag; + + if ((p = strrchr(p, '-')) && + (lang = av_convert_lang_to(p + 1, AV_LANG_ISO639_2_BIBL))) + *p = 0; + + p = key; + while (*p) { + if (*p == ' ') + *p = '_'; + else if (*p >= 'a' && *p <= 'z') + *p -= 'a' - 'A'; + p++; + } + + tag = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, 0); + put_ebml_string(pb, MATROSKA_ID_TAGNAME, key); + if (lang) + put_ebml_string(pb, MATROSKA_ID_TAGLANG, lang); + put_ebml_string(pb, MATROSKA_ID_TAGSTRING, t->value); + end_ebml_master(pb, tag); + + av_freep(&key); +} + +static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, unsigned int elementid, + unsigned int uid, ebml_master *tags) +{ + MatroskaMuxContext *mkv = s->priv_data; + ebml_master tag, targets; + AVDictionaryEntry *t = NULL; + int ret; + + if (!tags->pos) { + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TAGS, avio_tell(s->pb)); + if (ret < 0) return ret; + + *tags = start_ebml_master(s->pb, MATROSKA_ID_TAGS, 0); + } + + tag = start_ebml_master(s->pb, MATROSKA_ID_TAG, 0); + targets = start_ebml_master(s->pb, MATROSKA_ID_TAGTARGETS, 0); + if (elementid) + put_ebml_uint(s->pb, elementid, uid); + end_ebml_master(s->pb, targets); + + while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) + if (av_strcasecmp(t->key, "title") && av_strcasecmp(t->key, "stereo_mode")) + mkv_write_simpletag(s->pb, t); + + end_ebml_master(s->pb, tag); + return 0; +} + +static int mkv_write_tags(AVFormatContext *s) +{ + ebml_master tags = {0}; + int i, ret; + + ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL); + + if (av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + ret = mkv_write_tag(s, s->metadata, 0, 0, &tags); + if (ret < 0) return ret; + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + if (!av_dict_get(st->metadata, "", 0, AV_DICT_IGNORE_SUFFIX)) + continue; + + ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tags); + if (ret < 0) return ret; + } + + for (i = 0; i < s->nb_chapters; i++) { + AVChapter *ch = s->chapters[i]; + + if (!av_dict_get(ch->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) + continue; + + ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id, &tags); + if (ret < 0) return ret; + } + + if (tags.pos) + end_ebml_master(s->pb, tags); + return 0; +} + +static int mkv_write_attachments(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + ebml_master attachments; + AVLFG c; + int i, ret; + + if (!mkv->have_attachments) + return 0; + + av_lfg_init(&c, av_get_random_seed()); + + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_ATTACHMENTS, avio_tell(pb)); + if (ret < 0) return ret; + + attachments = start_ebml_master(pb, MATROSKA_ID_ATTACHMENTS, 0); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + ebml_master attached_file; + AVDictionaryEntry *t; + const char *mimetype = NULL; + uint64_t fileuid; + + if (st->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT) + continue; + + attached_file = start_ebml_master(pb, MATROSKA_ID_ATTACHEDFILE, 0); + + if (t = av_dict_get(st->metadata, "title", NULL, 0)) + put_ebml_string(pb, MATROSKA_ID_FILEDESC, t->value); + if (!(t = av_dict_get(st->metadata, "filename", NULL, 0))) { + av_log(s, AV_LOG_ERROR, "Attachment stream %d has no filename tag.\n", i); + return AVERROR(EINVAL); + } + put_ebml_string(pb, MATROSKA_ID_FILENAME, t->value); + if (t = av_dict_get(st->metadata, "mimetype", NULL, 0)) + mimetype = t->value; + else if (st->codec->codec_id != AV_CODEC_ID_NONE ) { + int i; + for (i = 0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) + if (ff_mkv_mime_tags[i].id == st->codec->codec_id) { + mimetype = ff_mkv_mime_tags[i].str; + break; + } + } + if (!mimetype) { + av_log(s, AV_LOG_ERROR, "Attachment stream %d has no mimetype tag and " + "it cannot be deduced from the codec id.\n", i); + return AVERROR(EINVAL); + } + + if (st->codec->flags & CODEC_FLAG_BITEXACT) { + struct AVSHA *sha = av_sha_alloc(); + uint8_t digest[20]; + if (!sha) + return AVERROR(ENOMEM); + av_sha_init(sha, 160); + av_sha_update(sha, st->codec->extradata, st->codec->extradata_size); + av_sha_final(sha, digest); + av_free(sha); + fileuid = AV_RL64(digest); + } else { + fileuid = av_lfg_get(&c); + } + av_log(s, AV_LOG_VERBOSE, "Using %.16"PRIx64" for attachment %d\n", + fileuid, i); + + put_ebml_string(pb, MATROSKA_ID_FILEMIMETYPE, mimetype); + put_ebml_binary(pb, MATROSKA_ID_FILEDATA, st->codec->extradata, st->codec->extradata_size); + put_ebml_uint(pb, MATROSKA_ID_FILEUID, fileuid); + end_ebml_master(pb, attached_file); + } + end_ebml_master(pb, attachments); + + return 0; +} + +static int mkv_write_header(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + ebml_master ebml_header, segment_info; + AVDictionaryEntry *tag; + int ret, i; + + if (!strcmp(s->oformat->name, "webm")) mkv->mode = MODE_WEBM; + else mkv->mode = MODE_MATROSKAv2; + + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_id == AV_CODEC_ID_ATRAC3 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_COOK || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RA_288 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_SIPR || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RV10 || + s->streams[i]->codec->codec_id == AV_CODEC_ID_RV20) { + av_log(s, AV_LOG_ERROR, + "The Matroska muxer does not yet support muxing %s\n", + avcodec_get_name(s->streams[i]->codec->codec_id)); + return AVERROR_PATCHWELCOME; + } + + mkv->tracks = av_mallocz(s->nb_streams * sizeof(*mkv->tracks)); + if (!mkv->tracks) + return AVERROR(ENOMEM); + + ebml_header = start_ebml_master(pb, EBML_ID_HEADER, 0); + put_ebml_uint (pb, EBML_ID_EBMLVERSION , 1); + put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1); + put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4); + put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8); + put_ebml_string (pb, EBML_ID_DOCTYPE , s->oformat->name); + put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2); + put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); + end_ebml_master(pb, ebml_header); + + mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT, 0); + mkv->segment_offset = avio_tell(pb); + + // we write 2 seek heads - one at the end of the file to point to each + // cluster, and one at the beginning to point to all other level one + // elements (including the seek head at the end of the file), which + // isn't more than 10 elements if we only write one of each other + // currently defined level 1 element + mkv->main_seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10); + if (!mkv->main_seekhead) + return AVERROR(ENOMEM); + + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_INFO, avio_tell(pb)); + if (ret < 0) return ret; + + segment_info = start_ebml_master(pb, MATROSKA_ID_INFO, 0); + put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000); + if ((tag = av_dict_get(s->metadata, "title", NULL, 0))) + put_ebml_string(pb, MATROSKA_ID_TITLE, tag->value); + if (!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) { + uint32_t segment_uid[4]; + AVLFG lfg; + + av_lfg_init(&lfg, av_get_random_seed()); + + for (i = 0; i < 4; i++) + segment_uid[i] = av_lfg_get(&lfg); + + put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , LIBAVFORMAT_IDENT); + put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT); + put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16); + } + + if (tag = av_dict_get(s->metadata, "creation_time", NULL, 0)) { + // Adjust time so it's relative to 2001-01-01 and convert to nanoseconds. + int64_t date_utc = (ff_iso8601_to_unix_time(tag->value) - 978307200) * 1000000000; + uint8_t date_utc_buf[8]; + AV_WB64(date_utc_buf, date_utc); + put_ebml_binary(pb, MATROSKA_ID_DATEUTC, date_utc_buf, 8); + } + + // reserve space for the duration + mkv->duration = 0; + mkv->duration_offset = avio_tell(pb); + put_ebml_void(pb, 11); // assumes double-precision float to be written + end_ebml_master(pb, segment_info); + + ret = mkv_write_tracks(s); + if (ret < 0) return ret; + + if (mkv->mode != MODE_WEBM) { + ret = mkv_write_chapters(s); + if (ret < 0) return ret; + + ret = mkv_write_tags(s); + if (ret < 0) return ret; + + ret = mkv_write_attachments(s); + if (ret < 0) return ret; + } + + if (!s->pb->seekable) + mkv_write_seekhead(pb, mkv->main_seekhead); + + mkv->cues = mkv_start_cues(mkv->segment_offset); + if (mkv->cues == NULL) + return AVERROR(ENOMEM); + + av_init_packet(&mkv->cur_audio_pkt); + mkv->cur_audio_pkt.size = 0; + mkv->cluster_pos = -1; + + avio_flush(pb); + return 0; +} + +static int mkv_blockgroup_size(int pkt_size) +{ + int size = pkt_size + 4; + size += ebml_num_size(size); + size += 2; // EBML ID for block and block duration + size += 8; // max size of block duration + size += ebml_num_size(size); + size += 1; // blockgroup EBML ID + return size; +} + +static int ass_get_duration(const uint8_t *p) +{ + int sh, sm, ss, sc, eh, em, es, ec; + uint64_t start, end; + + if (sscanf(p, "%*[^,],%d:%d:%d%*c%d,%d:%d:%d%*c%d", + &sh, &sm, &ss, &sc, &eh, &em, &es, &ec) != 8) + return 0; + start = 3600000LL*sh + 60000LL*sm + 1000LL*ss + 10LL*sc; + end = 3600000LL*eh + 60000LL*em + 1000LL*es + 10LL*ec; + return end - start; +} + +#if FF_API_ASS_SSA +static int mkv_write_ass_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + int i, layer = 0, max_duration = 0, size, line_size, data_size = pkt->size; + uint8_t *start, *end, *data = pkt->data; + ebml_master blockgroup; + char buffer[2048]; + + while (data_size) { + int duration = ass_get_duration(data); + max_duration = FFMAX(duration, max_duration); + end = memchr(data, '\n', data_size); + size = line_size = end ? end-data+1 : data_size; + size -= end ? (end[-1]=='\r')+1 : 0; + start = data; + for (i=0; i<3; i++, start++) + if (!(start = memchr(start, ',', size-(start-data)))) + return max_duration; + size -= start - data; + sscanf(data, "Dialogue: %d,", &layer); + i = snprintf(buffer, sizeof(buffer), "%"PRId64",%d,", + s->streams[pkt->stream_index]->nb_frames, layer); + size = FFMIN(i+size, sizeof(buffer)); + memcpy(buffer+i, start, size-i); + + av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, " + "pts %" PRId64 ", duration %d\n", + avio_tell(pb), size, pkt->pts, duration); + blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(size)); + put_ebml_id(pb, MATROSKA_ID_BLOCK); + put_ebml_num(pb, size+4, 0); + avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126 + avio_wb16(pb, pkt->pts - mkv->cluster_pts); + avio_w8(pb, 0); + avio_write(pb, buffer, size); + put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration); + end_ebml_master(pb, blockgroup); + + data += line_size; + data_size -= line_size; + } + + return max_duration; +} +#endif + +static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, + unsigned int blockid, AVPacket *pkt, int flags) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + uint8_t *data = NULL; + int offset = 0, size = pkt->size; + int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + + av_log(s, AV_LOG_DEBUG, "Writing block at offset %" PRIu64 ", size %d, " + "pts %" PRId64 ", dts %" PRId64 ", duration %d, flags %d\n", + avio_tell(pb), pkt->size, pkt->pts, pkt->dts, pkt->duration, flags); + if (codec->codec_id == AV_CODEC_ID_H264 && codec->extradata_size > 0 && + (AV_RB24(codec->extradata) == 1 || AV_RB32(codec->extradata) == 1)) + ff_avc_parse_nal_units_buf(pkt->data, &data, &size); + else + data = pkt->data; + + if (codec->codec_id == AV_CODEC_ID_PRORES) { + /* Matroska specification requires to remove the first QuickTime atom + */ + size -= 8; + offset = 8; + } + + put_ebml_id(pb, blockid); + put_ebml_num(pb, size+4, 0); + avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126 + avio_wb16(pb, ts - mkv->cluster_pts); + avio_w8(pb, flags); + avio_write(pb, data + offset, size); + if (data != pkt->data) + av_free(data); +} + +static int srt_get_duration(uint8_t **buf) +{ + int i, duration = 0; + + for (i=0; i<2 && !duration; i++) { + int s_hour, s_min, s_sec, s_hsec, e_hour, e_min, e_sec, e_hsec; + if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d", + &s_hour, &s_min, &s_sec, &s_hsec, + &e_hour, &e_min, &e_sec, &e_hsec) == 8) { + s_min += 60*s_hour; e_min += 60*e_hour; + s_sec += 60*s_min; e_sec += 60*e_min; + s_hsec += 1000*s_sec; e_hsec += 1000*e_sec; + duration = e_hsec - s_hsec; + } + *buf += strcspn(*buf, "\n") + 1; + } + return duration; +} + +static int mkv_write_srt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +{ + ebml_master blockgroup; + AVPacket pkt2 = *pkt; + int64_t duration = srt_get_duration(&pkt2.data); + pkt2.size -= pkt2.data - pkt->data; + + blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, + mkv_blockgroup_size(pkt2.size)); + mkv_write_block(s, pb, MATROSKA_ID_BLOCK, &pkt2, 0); + put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration); + end_ebml_master(pb, blockgroup); + + return duration; +} + +static void mkv_flush_dynbuf(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + int bufsize; + uint8_t *dyn_buf; + + if (!mkv->dyn_bc) + return; + + bufsize = avio_close_dyn_buf(mkv->dyn_bc, &dyn_buf); + avio_write(s->pb, dyn_buf, bufsize); + av_free(dyn_buf); + mkv->dyn_bc = NULL; +} + +static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + int keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY); + int duration = pkt->duration; + int ret; + int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + + if (ts == AV_NOPTS_VALUE) { + av_log(s, AV_LOG_ERROR, "Can't write packet with unknown timestamp\n"); + return AVERROR(EINVAL); + } + + if (!s->pb->seekable) { + if (!mkv->dyn_bc) { + if ((ret = avio_open_dyn_buf(&mkv->dyn_bc)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open dynamic buffer\n"); + return ret; + } + } + pb = mkv->dyn_bc; + } + + if (mkv->cluster_pos == -1) { + mkv->cluster_pos = avio_tell(s->pb); + mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER, 0); + put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts)); + mkv->cluster_pts = FFMAX(0, ts); + } + + if (codec->codec_type != AVMEDIA_TYPE_SUBTITLE) { + mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe << 7); +#if FF_API_ASS_SSA + } else if (codec->codec_id == AV_CODEC_ID_SSA) { + duration = mkv_write_ass_blocks(s, pb, pkt); +#endif + } else if (codec->codec_id == AV_CODEC_ID_SRT) { + duration = mkv_write_srt_blocks(s, pb, pkt); + } else { + ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(pkt->size)); + /* For backward compatibility, prefer convergence_duration. */ + if (pkt->convergence_duration > 0) { + duration = pkt->convergence_duration; + } + mkv_write_block(s, pb, MATROSKA_ID_BLOCK, pkt, 0); + put_ebml_uint(pb, MATROSKA_ID_BLOCKDURATION, duration); + end_ebml_master(pb, blockgroup); + } + + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe) { + ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, ts, mkv->cluster_pos); + if (ret < 0) return ret; + } + + mkv->duration = FFMAX(mkv->duration, ts + duration); + return 0; +} + +static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb->seekable ? s->pb : mkv->dyn_bc; + AVCodecContext *codec = s->streams[pkt->stream_index]->codec; + int ret, keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY); + int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + int cluster_size = avio_tell(pb) - (s->pb->seekable ? mkv->cluster_pos : 0); + + // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or + // after 4k and on a keyframe + if (mkv->cluster_pos != -1 && + ((!s->pb->seekable && (cluster_size > 32*1024 || ts > mkv->cluster_pts + 1000)) + || cluster_size > 5*1024*1024 || ts > mkv->cluster_pts + 5000 + || (codec->codec_type == AVMEDIA_TYPE_VIDEO && keyframe && cluster_size > 4*1024))) { + av_log(s, AV_LOG_DEBUG, "Starting new cluster at offset %" PRIu64 + " bytes, pts %" PRIu64 "\n", avio_tell(pb), ts); + end_ebml_master(pb, mkv->cluster); + mkv->cluster_pos = -1; + if (mkv->dyn_bc) + mkv_flush_dynbuf(s); + } + + // check if we have an audio packet cached + if (mkv->cur_audio_pkt.size > 0) { + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + av_free_packet(&mkv->cur_audio_pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); + return ret; + } + } + + // buffer an audio packet to ensure the packet containing the video + // keyframe's timecode is contained in the same cluster for WebM + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mkv->cur_audio_pkt = *pkt; + mkv->cur_audio_pkt.buf = av_buffer_ref(pkt->buf); + ret = mkv->cur_audio_pkt.buf ? 0 : AVERROR(ENOMEM); + } else + ret = mkv_write_packet_internal(s, pkt); + return ret; +} + +static int mkv_write_trailer(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *pb = s->pb; + int64_t currentpos, cuespos; + int ret; + + // check if we have an audio packet cached + if (mkv->cur_audio_pkt.size > 0) { + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); + av_free_packet(&mkv->cur_audio_pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); + return ret; + } + } + + if (mkv->dyn_bc) { + end_ebml_master(mkv->dyn_bc, mkv->cluster); + mkv_flush_dynbuf(s); + } else if (mkv->cluster_pos != -1) { + end_ebml_master(pb, mkv->cluster); + } + + if (pb->seekable) { + if (mkv->cues->num_entries) { + cuespos = mkv_write_cues(pb, mkv->cues, mkv->tracks, s->nb_streams); + + ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES, cuespos); + if (ret < 0) return ret; + } + + mkv_write_seekhead(pb, mkv->main_seekhead); + + // update the duration + av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration); + currentpos = avio_tell(pb); + avio_seek(pb, mkv->duration_offset, SEEK_SET); + put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration); + + avio_seek(pb, currentpos, SEEK_SET); + } + + end_ebml_master(pb, mkv->segment); + av_free(mkv->tracks); + av_freep(&mkv->cues->entries); + av_freep(&mkv->cues); + + return 0; +} + +static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance) +{ + int i; + for (i = 0; ff_mkv_codec_tags[i].id != AV_CODEC_ID_NONE; i++) + if (ff_mkv_codec_tags[i].id == codec_id) + return 1; + + if (std_compliance < FF_COMPLIANCE_NORMAL) { // mkv theoretically supports any + enum AVMediaType type = avcodec_get_type(codec_id); // video/audio through VFW/ACM + if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO) + return 1; + } + + return 0; +} + +const AVCodecTag additional_audio_tags[] = { + { AV_CODEC_ID_ALAC, 0XFFFFFFFF }, + { AV_CODEC_ID_EAC3, 0XFFFFFFFF }, + { AV_CODEC_ID_MLP, 0xFFFFFFFF }, + { AV_CODEC_ID_OPUS, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S16BE, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S24BE, 0xFFFFFFFF }, + { AV_CODEC_ID_PCM_S32BE, 0xFFFFFFFF }, + { AV_CODEC_ID_QDM2, 0xFFFFFFFF }, + { AV_CODEC_ID_RA_144, 0xFFFFFFFF }, + { AV_CODEC_ID_RA_288, 0xFFFFFFFF }, + { AV_CODEC_ID_COOK, 0xFFFFFFFF }, + { AV_CODEC_ID_TRUEHD, 0xFFFFFFFF }, + { AV_CODEC_ID_WAVPACK, 0xFFFFFFFF }, + { AV_CODEC_ID_NONE, 0xFFFFFFFF } +}; + +const AVCodecTag additional_video_tags[] = { + { AV_CODEC_ID_PRORES, 0xFFFFFFFF }, + { AV_CODEC_ID_RV10, 0xFFFFFFFF }, + { AV_CODEC_ID_RV20, 0xFFFFFFFF }, + { AV_CODEC_ID_RV30, 0xFFFFFFFF }, + { AV_CODEC_ID_RV40, 0xFFFFFFFF }, + { AV_CODEC_ID_VP9, 0xFFFFFFFF }, + { AV_CODEC_ID_NONE, 0xFFFFFFFF } +}; + +#if CONFIG_MATROSKA_MUXER +AVOutputFormat ff_matroska_muxer = { + .name = "matroska", + .long_name = NULL_IF_CONFIG_SMALL("Matroska"), + .mime_type = "video/x-matroska", + .extensions = "mkv", + .priv_data_size = sizeof(MatroskaMuxContext), + .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3, + .video_codec = CONFIG_LIBX264_ENCODER ? + AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, + .write_header = mkv_write_header, + .write_packet = mkv_write_packet, + .write_trailer = mkv_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + AVFMT_TS_NONSTRICT, + .codec_tag = (const AVCodecTag* const []){ + ff_codec_bmp_tags, ff_codec_wav_tags, + additional_audio_tags, additional_video_tags, 0 + }, +#if FF_API_ASS_SSA + .subtitle_codec = AV_CODEC_ID_SSA, +#else + .subtitle_codec = AV_CODEC_ID_ASS, +#endif + .query_codec = mkv_query_codec, +}; +#endif + +#if CONFIG_WEBM_MUXER +AVOutputFormat ff_webm_muxer = { + .name = "webm", + .long_name = NULL_IF_CONFIG_SMALL("WebM"), + .mime_type = "video/webm", + .extensions = "webm", + .priv_data_size = sizeof(MatroskaMuxContext), + .audio_codec = AV_CODEC_ID_VORBIS, + .video_codec = AV_CODEC_ID_VP8, + .write_header = mkv_write_header, + .write_packet = mkv_write_packet, + .write_trailer = mkv_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | + AVFMT_TS_NONSTRICT, +}; +#endif + +#if CONFIG_MATROSKA_AUDIO_MUXER +AVOutputFormat ff_matroska_audio_muxer = { + .name = "matroska", + .long_name = NULL_IF_CONFIG_SMALL("Matroska"), + .mime_type = "audio/x-matroska", + .extensions = "mka", + .priv_data_size = sizeof(MatroskaMuxContext), + .audio_codec = CONFIG_LIBVORBIS_ENCODER ? + AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3, + .video_codec = AV_CODEC_ID_NONE, + .write_header = mkv_write_header, + .write_packet = mkv_write_packet, + .write_trailer = mkv_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT, + .codec_tag = (const AVCodecTag* const []){ + ff_codec_wav_tags, additional_audio_tags, 0 + }, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskaenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/matroskaenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,29 @@ +libavformat/matroskaenc.o libavformat/matroskaenc.o: \ + libavformat/matroskaenc.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/riff.h libavformat/metadata.h libavformat/isom.h \ + libavformat/dv.h libavformat/matroska.h libavformat/avc.h \ + libavformat/flacenc.h libavcodec/flac.h libavcodec/avcodec.h \ + libavcodec/get_bits.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavcodec/bytestream.h libavformat/avlanguage.h \ + libavutil/samplefmt.h libavutil/sha.h libavutil/intreadwrite.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/random_seed.h \ + libavutil/lfg.h libavutil/avstring.h libavcodec/xiph.h \ + libavcodec/mpeg4audio.h libavcodec/put_bits.h libavutil/bswap.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/matroskaenc.o Binary file ffmpeg/libavformat/matroskaenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5enc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/md5enc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,130 @@ +/* + * MD5 encoder (for codec/format testing) + * Copyright (c) 2009 Reimar Döffinger, based on crcenc (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/md5.h" +#include "avformat.h" +#include "internal.h" + +struct MD5Context { + struct AVMD5 *md5; +}; + +static void md5_finish(struct AVFormatContext *s, char *buf) +{ + struct MD5Context *c = s->priv_data; + uint8_t md5[16]; + int i, offset = strlen(buf); + av_md5_final(c->md5, md5); + for (i = 0; i < sizeof(md5); i++) { + snprintf(buf + offset, 3, "%02"PRIx8, md5[i]); + offset += 2; + } + buf[offset] = '\n'; + buf[offset+1] = 0; + + avio_write(s->pb, buf, strlen(buf)); + avio_flush(s->pb); +} + +#if CONFIG_MD5_MUXER +static int write_header(struct AVFormatContext *s) +{ + struct MD5Context *c = s->priv_data; + c->md5 = av_md5_alloc(); + if (!c->md5) + return AVERROR(ENOMEM); + av_md5_init(c->md5); + return 0; +} + +static int write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + struct MD5Context *c = s->priv_data; + av_md5_update(c->md5, pkt->data, pkt->size); + return 0; +} + +static int write_trailer(struct AVFormatContext *s) +{ + struct MD5Context *c = s->priv_data; + char buf[64] = "MD5="; + + md5_finish(s, buf); + + av_freep(&c->md5); + return 0; +} + +AVOutputFormat ff_md5_muxer = { + .name = "md5", + .long_name = NULL_IF_CONFIG_SMALL("MD5 testing"), + .priv_data_size = sizeof(struct MD5Context), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = write_header, + .write_packet = write_packet, + .write_trailer = write_trailer, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_FRAMEMD5_MUXER +static int framemd5_write_header(struct AVFormatContext *s) +{ + struct MD5Context *c = s->priv_data; + c->md5 = av_md5_alloc(); + if (!c->md5) + return AVERROR(ENOMEM); + return ff_framehash_write_header(s); +} + +static int framemd5_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + struct MD5Context *c = s->priv_data; + char buf[256]; + av_md5_init(c->md5); + av_md5_update(c->md5, pkt->data, pkt->size); + + snprintf(buf, sizeof(buf) - 64, "%d, %10"PRId64", %10"PRId64", %8d, %8d, ", + pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size); + md5_finish(s, buf); + return 0; +} + +static int framemd5_write_trailer(struct AVFormatContext *s) +{ + struct MD5Context *c = s->priv_data; + av_freep(&c->md5); + return 0; +} + +AVOutputFormat ff_framemd5_muxer = { + .name = "framemd5", + .long_name = NULL_IF_CONFIG_SMALL("Per-frame MD5 testing"), + .priv_data_size = sizeof(struct MD5Context), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = framemd5_write_header, + .write_packet = framemd5_write_packet, + .write_trailer = framemd5_write_trailer, + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5enc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/md5enc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/md5enc.o libavformat/md5enc.o: libavformat/md5enc.c \ + libavutil/md5.h libavutil/attributes.h libavutil/version.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5enc.o Binary file ffmpeg/libavformat/md5enc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5proto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/md5proto.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavutil/avstring.h" +#include "libavutil/md5.h" +#include "libavutil/mem.h" +#include "libavutil/error.h" +#include "avformat.h" +#include "avio.h" +#include "url.h" + +struct MD5Context { + struct AVMD5 *md5; +}; + +static int md5_open(URLContext *h, const char *filename, int flags) +{ + struct MD5Context *c = h->priv_data; + + if (!(flags & AVIO_FLAG_WRITE)) + return AVERROR(EINVAL); + + c->md5 = av_md5_alloc(); + if (!c->md5) + return AVERROR(ENOMEM); + av_md5_init(c->md5); + + return 0; +} + +static int md5_write(URLContext *h, const unsigned char *buf, int size) +{ + struct MD5Context *c = h->priv_data; + av_md5_update(c->md5, buf, size); + return size; +} + +static int md5_close(URLContext *h) +{ + struct MD5Context *c = h->priv_data; + const char *filename = h->filename; + uint8_t md5[16], buf[64]; + URLContext *out; + int i, err = 0; + + av_md5_final(c->md5, md5); + for (i = 0; i < sizeof(md5); i++) + snprintf(buf + i*2, 3, "%02x", md5[i]); + buf[i*2] = '\n'; + + av_strstart(filename, "md5:", &filename); + + if (*filename) { + err = ffurl_open(&out, filename, AVIO_FLAG_WRITE, + &h->interrupt_callback, NULL); + if (err) + return err; + err = ffurl_write(out, buf, i*2+1); + ffurl_close(out); + } else { + if (fwrite(buf, 1, i*2+1, stdout) < i*2+1) + err = AVERROR(errno); + } + + av_freep(&c->md5); + + return err; +} + + +URLProtocol ff_md5_protocol = { + .name = "md5", + .url_open = md5_open, + .url_write = md5_write, + .url_close = md5_close, + .priv_data_size = sizeof(struct MD5Context), +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5proto.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/md5proto.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/md5proto.o libavformat/md5proto.o: libavformat/md5proto.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/md5.h \ + libavutil/version.h libavutil/mem.h libavutil/error.h \ + libavutil/avutil.h libavutil/common.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/error.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/md5proto.o Binary file ffmpeg/libavformat/md5proto.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/metadata.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/metadata.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,70 @@ +/* + * copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "metadata.h" +#include "libavutil/dict.h" +#include "libavutil/avstring.h" + +void ff_metadata_conv(AVDictionary **pm, const AVMetadataConv *d_conv, + const AVMetadataConv *s_conv) +{ + /* TODO: use binary search to look up the two conversion tables + if the tables are getting big enough that it would matter speed wise */ + const AVMetadataConv *sc, *dc; + AVDictionaryEntry *mtag = NULL; + AVDictionary *dst = NULL; + const char *key; + + if (d_conv == s_conv) + return; + + while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) { + key = mtag->key; + if (s_conv) + for (sc=s_conv; sc->native; sc++) + if (!av_strcasecmp(key, sc->native)) { + key = sc->generic; + break; + } + if (d_conv) + for (dc=d_conv; dc->native; dc++) + if (!av_strcasecmp(key, dc->generic)) { + key = dc->native; + break; + } + av_dict_set(&dst, key, mtag->value, 0); + } + av_dict_free(pm); + *pm = dst; +} + +void ff_metadata_conv_ctx(AVFormatContext *ctx, const AVMetadataConv *d_conv, + const AVMetadataConv *s_conv) +{ + int i; + ff_metadata_conv(&ctx->metadata, d_conv, s_conv); + for (i=0; inb_streams ; i++) + ff_metadata_conv(&ctx->streams [i]->metadata, d_conv, s_conv); + for (i=0; inb_chapters; i++) + ff_metadata_conv(&ctx->chapters[i]->metadata, d_conv, s_conv); + for (i=0; inb_programs; i++) + ff_metadata_conv(&ctx->programs[i]->metadata, d_conv, s_conv); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/metadata.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/metadata.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/metadata.o libavformat/metadata.o: libavformat/metadata.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/metadata.h \ + libavutil/avstring.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/metadata.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/metadata.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,44 @@ +/* + * copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_METADATA_H +#define AVFORMAT_METADATA_H + +/** + * @file + * internal metadata API header + * see avformat.h or the public API! + */ + + +#include "avformat.h" +#include "libavutil/dict.h" + +typedef struct AVMetadataConv { + const char *native; + const char *generic; +} AVMetadataConv; + +void ff_metadata_conv(AVDictionary **pm, const AVMetadataConv *d_conv, + const AVMetadataConv *s_conv); +void ff_metadata_conv_ctx(AVFormatContext *ctx, const AVMetadataConv *d_conv, + const AVMetadataConv *s_conv); + +#endif /* AVFORMAT_METADATA_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/metadata.o Binary file ffmpeg/libavformat/metadata.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mgsts.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mgsts.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,106 @@ +/* + * Metar Gear Solid: The Twin Snakes demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "riff.h" + +static int read_probe(AVProbeData *p) +{ + if (AV_RB32(p->buf ) != 0x000E || + AV_RB32(p->buf + 4) != 0x0050 || + AV_RB32(p->buf + 12) != 0x0034) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + AVRational fps; + uint32_t chunk_size; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + if (chunk_size != 80) + return AVERROR(EIO); + avio_skip(pb, 20); + + st = avformat_new_stream(s, 0); + if (!st) + return AVERROR(ENOMEM); + + st->need_parsing = AVSTREAM_PARSE_HEADERS; + st->start_time = 0; + st->nb_frames = + st->duration = avio_rb32(pb); + fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); + st->codec->width = avio_rb32(pb); + st->codec->height = avio_rb32(pb); + avio_skip(pb, 12); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = avio_rb32(pb); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + st->codec->codec_tag); + avpriv_set_pts_info(st, 64, fps.den, fps.num); + avio_skip(pb, 20); + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + uint32_t chunk_size, payload_size; + int ret; + + if (url_feof(pb)) + return AVERROR_EOF; + + avio_skip(pb, 4); + chunk_size = avio_rb32(pb); + avio_skip(pb, 4); + payload_size = avio_rb32(pb); + + if (chunk_size < payload_size + 16) + return AVERROR(EIO); + + ret = av_get_packet(pb, pkt, payload_size); + if (ret < 0) + return ret; + + pkt->pos -= 16; + pkt->duration = 1; + avio_skip(pb, chunk_size - (ret + 16)); + + return ret; +} + +AVInputFormat ff_mgsts_demuxer = { + .name = "mgsts", + .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mgsts.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mgsts.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/mgsts.o libavformat/mgsts.o: libavformat/mgsts.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/riff.h \ + libavformat/internal.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mgsts.o Binary file ffmpeg/libavformat/mgsts.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvddec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/microdvddec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,167 @@ +/* + * MicroDVD subtitle demuxer + * Copyright (c) 2010 Aurelien Jacobs + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/intreadwrite.h" + +#define MAX_LINESIZE 2048 + + +typedef struct { + FFDemuxSubtitlesQueue q; +} MicroDVDContext; + + +static int microdvd_probe(AVProbeData *p) +{ + unsigned char c; + const uint8_t *ptr = p->buf; + int i; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + + for (i=0; i<3; i++) { + if (sscanf(ptr, "{%*d}{}%c", &c) != 1 && + sscanf(ptr, "{%*d}{%*d}%c", &c) != 1 && + sscanf(ptr, "{DEFAULT}{}%c", &c) != 1) + return 0; + ptr += strcspn(ptr, "\n") + 1; + } + return AVPROBE_SCORE_MAX; +} + +static int64_t get_pts(const char *buf) +{ + int frame; + char c; + + if (sscanf(buf, "{%d}{%c", &frame, &c) == 2) + return frame; + return AV_NOPTS_VALUE; +} + +static int get_duration(const char *buf) +{ + int frame_start, frame_end; + + if (sscanf(buf, "{%d}{%d}", &frame_start, &frame_end) == 2) + return frame_end - frame_start; + return -1; +} + +static int microdvd_read_header(AVFormatContext *s) +{ + AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ + MicroDVDContext *microdvd = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int i = 0; + char line[MAX_LINESIZE]; + + if (!st) + return AVERROR(ENOMEM); + + while (!url_feof(s->pb)) { + char *p = line; + AVPacket *sub; + int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + line[strcspn(line, "\r\n")] = 0; + if (i++ < 3) { + int frame; + double fps; + char c; + + if ((sscanf(line, "{%d}{}%6lf", &frame, &fps) == 2 || + sscanf(line, "{%d}{%*d}%6lf", &frame, &fps) == 2) + && frame <= 1 && fps > 3 && fps < 100) + pts_info = av_d2q(fps, 100000); + if (!st->codec->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) { + st->codec->extradata = av_strdup(line + 11); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = strlen(st->codec->extradata) + 1; + continue; + } + } +#define SKIP_FRAME_ID \ + p = strchr(p, '}'); \ + if (!p) { \ + av_log(s, AV_LOG_WARNING, "Invalid event \"%s\"" \ + " at line %d\n", line, i); \ + continue; \ + } \ + p++ + SKIP_FRAME_ID; + SKIP_FRAME_ID; + if (!*p) + continue; + sub = ff_subtitles_queue_insert(µdvd->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = get_pts(line); + sub->duration = get_duration(line); + } + ff_subtitles_queue_finalize(µdvd->q); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_MICRODVD; + return 0; +} + +static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MicroDVDContext *microdvd = s->priv_data; + return ff_subtitles_queue_read_packet(µdvd->q, pkt); +} + +static int microdvd_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MicroDVDContext *microdvd = s->priv_data; + return ff_subtitles_queue_seek(µdvd->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int microdvd_read_close(AVFormatContext *s) +{ + MicroDVDContext *microdvd = s->priv_data; + ff_subtitles_queue_clean(µdvd->q); + return 0; +} + +AVInputFormat ff_microdvd_demuxer = { + .name = "microdvd", + .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .priv_data_size = sizeof(MicroDVDContext), + .read_probe = microdvd_probe, + .read_header = microdvd_read_header, + .read_packet = microdvd_read_packet, + .read_seek2 = microdvd_read_seek, + .read_close = microdvd_read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvddec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/microdvddec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/microdvddec.o libavformat/microdvddec.o: \ + libavformat/microdvddec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvddec.o Binary file ffmpeg/libavformat/microdvddec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvdenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/microdvdenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,67 @@ +/* + * MicroDVD subtitle muxer + * Copyright (c) 2010 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "avformat.h" +#include "internal.h" + +static int microdvd_write_header(struct AVFormatContext *s) +{ + AVCodecContext *avctx = s->streams[0]->codec; + AVRational tb = avctx->time_base; + + if (s->nb_streams != 1 || avctx->codec_id != AV_CODEC_ID_MICRODVD) { + av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n"); + return -1; + } + + if (avctx->extradata && avctx->extradata_size > 0) { + avio_write(s->pb, "{DEFAULT}{}", 11); + avio_write(s->pb, avctx->extradata, avctx->extradata_size); + avio_flush(s->pb); + } + + avpriv_set_pts_info(s->streams[0], 64, tb.num, tb.den); + return 0; +} + +static int microdvd_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ + avio_printf(avf->pb, "{%"PRId64"}", pkt->pts); + if (pkt->duration < 0) + avio_write(avf->pb, "{}", 2); + else + avio_printf(avf->pb, "{%"PRId64"}", pkt->pts + pkt->duration); + avio_write(avf->pb, pkt->data, pkt->size); + avio_write(avf->pb, "\n", 1); + return 0; +} + +AVOutputFormat ff_microdvd_muxer = { + .name = "microdvd", + .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .mime_type = "text/x-microdvd", + .extensions = "sub", + .write_header = microdvd_write_header, + .write_packet = microdvd_write_packet, + .flags = AVFMT_NOTIMESTAMPS, + .subtitle_codec = AV_CODEC_ID_MICRODVD, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvdenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/microdvdenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/microdvdenc.o libavformat/microdvdenc.o: \ + libavformat/microdvdenc.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/microdvdenc.o Binary file ffmpeg/libavformat/microdvdenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mkvtimestamp_v2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mkvtimestamp_v2.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,50 @@ +/* + * extract pts as timecode v2, as defined by mkvtoolnix + * Copyright (c) 2009 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" + +static int write_header(AVFormatContext *s) +{ + static const char *header = "# timecode format v2\n"; + avio_write(s->pb, header, strlen(header)); + avpriv_set_pts_info(s->streams[0], 64, 1, 1000); + return 0; +} + +static int write_packet(AVFormatContext *s, AVPacket *pkt) +{ + char buf[256]; + if (pkt->stream_index) + av_log(s, AV_LOG_WARNING, "More than one stream unsupported\n"); + snprintf(buf, sizeof(buf), "%" PRId64 "\n", pkt->dts); + avio_write(s->pb, buf, strlen(buf)); + return 0; +} + +AVOutputFormat ff_mkvtimestamp_v2_muxer = { + .name = "mkvtimestamp_v2", + .long_name = NULL_IF_CONFIG_SMALL("extract pts as timecode v2 format, as defined by mkvtoolnix"), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = write_header, + .write_packet = write_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mkvtimestamp_v2.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mkvtimestamp_v2.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/mkvtimestamp_v2.o libavformat/mkvtimestamp_v2.o: \ + libavformat/mkvtimestamp_v2.c libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mkvtimestamp_v2.o Binary file ffmpeg/libavformat/mkvtimestamp_v2.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,197 @@ +/* + * American Laser Games MM Format Demuxer + * Copyright (c) 2006 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * American Laser Games MM Format Demuxer + * by Peter Ross (pross@xvid.org) + * + * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games, + * including Mad Dog McCree and Crime Patrol. + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define MM_PREAMBLE_SIZE 6 + +#define MM_TYPE_HEADER 0x0 +#define MM_TYPE_INTER 0x5 +#define MM_TYPE_INTRA 0x8 +#define MM_TYPE_INTRA_HH 0xc +#define MM_TYPE_INTER_HH 0xd +#define MM_TYPE_INTRA_HHV 0xe +#define MM_TYPE_INTER_HHV 0xf +#define MM_TYPE_AUDIO 0x15 +#define MM_TYPE_PALETTE 0x31 + +#define MM_HEADER_LEN_V 0x16 /* video only */ +#define MM_HEADER_LEN_AV 0x18 /* video + audio */ + +#define MM_PALETTE_COUNT 128 +#define MM_PALETTE_SIZE (MM_PALETTE_COUNT*3) + +typedef struct { + unsigned int audio_pts, video_pts; +} MmDemuxContext; + +static int probe(AVProbeData *p) +{ + int len, type, fps, w, h; + if (p->buf_size < MM_HEADER_LEN_AV + MM_PREAMBLE_SIZE) + return 0; + /* the first chunk is always the header */ + if (AV_RL16(&p->buf[0]) != MM_TYPE_HEADER) + return 0; + len = AV_RL32(&p->buf[2]); + if (len != MM_HEADER_LEN_V && len != MM_HEADER_LEN_AV) + return 0; + fps = AV_RL16(&p->buf[8]); + w = AV_RL16(&p->buf[12]); + h = AV_RL16(&p->buf[14]); + if (!fps || fps > 60 || !w || w > 2048 || !h || h > 2048) + return 0; + type = AV_RL16(&p->buf[len]); + if (!type || type > 0x31) + return 0; + + /* only return half certainty since this check is a bit sketchy */ + return AVPROBE_SCORE_MAX / 2; +} + +static int read_header(AVFormatContext *s) +{ + MmDemuxContext *mm = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + + unsigned int type, length; + unsigned int frame_rate, width, height; + + type = avio_rl16(pb); + length = avio_rl32(pb); + + if (type != MM_TYPE_HEADER) + return AVERROR_INVALIDDATA; + + /* read header */ + avio_rl16(pb); /* total number of chunks */ + frame_rate = avio_rl16(pb); + avio_rl16(pb); /* ibm-pc video bios mode */ + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_skip(pb, length - 10); /* unknown data */ + + /* video stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MMVIDEO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = width; + st->codec->height = height; + avpriv_set_pts_info(st, 64, 1, frame_rate); + + /* audio stream */ + if (length == MM_HEADER_LEN_AV) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 8000; + avpriv_set_pts_info(st, 64, 1, 8000); /* 8000 hz */ + } + + mm->audio_pts = 0; + mm->video_pts = 0; + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MmDemuxContext *mm = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char preamble[MM_PREAMBLE_SIZE]; + unsigned int type, length; + + while(1) { + + if (avio_read(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) { + return AVERROR(EIO); + } + + type = AV_RL16(&preamble[0]); + length = AV_RL16(&preamble[2]); + + switch(type) { + case MM_TYPE_PALETTE : + case MM_TYPE_INTER : + case MM_TYPE_INTRA : + case MM_TYPE_INTRA_HH : + case MM_TYPE_INTER_HH : + case MM_TYPE_INTRA_HHV : + case MM_TYPE_INTER_HHV : + /* output preamble + data */ + if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE)) + return AVERROR(ENOMEM); + memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE); + if (avio_read(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length) + return AVERROR(EIO); + pkt->size = length + MM_PREAMBLE_SIZE; + pkt->stream_index = 0; + pkt->pts = mm->video_pts; + if (type!=MM_TYPE_PALETTE) + mm->video_pts++; + return 0; + + case MM_TYPE_AUDIO : + if (av_get_packet(s->pb, pkt, length)<0) + return AVERROR(ENOMEM); + pkt->stream_index = 1; + pkt->pts = mm->audio_pts++; + return 0; + + default : + av_log(s, AV_LOG_INFO, "unknown chunk type 0x%x\n", type); + avio_skip(pb, length); + } + } +} + +AVInputFormat ff_mm_demuxer = { + .name = "mm", + .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM"), + .priv_data_size = sizeof(MmDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/mm.o libavformat/mm.o: libavformat/mm.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mm.o Binary file ffmpeg/libavformat/mm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,319 @@ +/* + * Yamaha SMAF format + * Copyright (c) 2005 Vidar Madsen + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "pcm.h" +#include "rawenc.h" +#include "riff.h" + +typedef struct { + int64_t atrpos, atsqpos, awapos; + int64_t data_end; + int stereo; +} MMFContext; + +static const int mmf_rates[] = { 4000, 8000, 11025, 22050, 44100 }; + +static int mmf_rate(int code) +{ + if((code < 0) || (code > 4)) + return -1; + return mmf_rates[code]; +} + +#if CONFIG_MMF_MUXER +static int mmf_rate_code(int rate) +{ + int i; + for(i = 0; i < 5; i++) + if(mmf_rates[i] == rate) + return i; + return -1; +} + +/* Copy of end_tag() from avienc.c, but for big-endian chunk size */ +static void end_tag_be(AVIOContext *pb, int64_t start) +{ + int64_t pos; + + pos = avio_tell(pb); + avio_seek(pb, start - 4, SEEK_SET); + avio_wb32(pb, (uint32_t)(pos - start)); + avio_seek(pb, pos, SEEK_SET); +} + +static int mmf_write_header(AVFormatContext *s) +{ + MMFContext *mmf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int rate; + const char *version = s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT ? + "VN:Lavf," : + "VN:"LIBAVFORMAT_IDENT","; + + rate = mmf_rate_code(s->streams[0]->codec->sample_rate); + if(rate < 0) { + av_log(s, AV_LOG_ERROR, "Unsupported sample rate %d, supported are 4000, 8000, 11025, 22050 and 44100\n", s->streams[0]->codec->sample_rate); + return AVERROR(EINVAL); + } + + mmf->stereo = s->streams[0]->codec->channels > 1; + if (mmf->stereo && + s->streams[0]->codec->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + av_log(s, AV_LOG_ERROR, "Yamaha SMAF stereo is experimental, " + "add '-strict %d' if you want to use it.\n", + FF_COMPLIANCE_EXPERIMENTAL); + return AVERROR(EINVAL); + } + + ffio_wfourcc(pb, "MMMD"); + avio_wb32(pb, 0); + pos = ff_start_tag(pb, "CNTI"); + avio_w8(pb, 0); /* class */ + avio_w8(pb, 1); /* type */ + avio_w8(pb, 1); /* code type */ + avio_w8(pb, 0); /* status */ + avio_w8(pb, 0); /* counts */ + end_tag_be(pb, pos); + pos = ff_start_tag(pb, "OPDA"); + avio_write(pb, version, strlen(version)); /* metadata ("ST:songtitle,VN:version,...") */ + end_tag_be(pb, pos); + + avio_write(pb, "ATR\x00", 4); + avio_wb32(pb, 0); + mmf->atrpos = avio_tell(pb); + avio_w8(pb, 0); /* format type */ + avio_w8(pb, 0); /* sequence type */ + avio_w8(pb, (mmf->stereo << 7) | (1 << 4) | rate); /* (channel << 7) | (format << 4) | rate */ + avio_w8(pb, 0); /* wave base bit */ + avio_w8(pb, 2); /* time base d */ + avio_w8(pb, 2); /* time base g */ + + ffio_wfourcc(pb, "Atsq"); + avio_wb32(pb, 16); + mmf->atsqpos = avio_tell(pb); + /* Will be filled on close */ + avio_write(pb, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); + + mmf->awapos = ff_start_tag(pb, "Awa\x01"); + + avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codec->sample_rate); + + avio_flush(pb); + + return 0; +} + +/* Write a variable-length symbol */ +static void put_varlength(AVIOContext *pb, int val) +{ + if(val < 128) + avio_w8(pb, val); + else { + val -= 128; + avio_w8(pb, 0x80 | val >> 7); + avio_w8(pb, 0x7f & val); + } +} + +static int mmf_write_trailer(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + MMFContext *mmf = s->priv_data; + int64_t pos, size; + int gatetime; + + if (s->pb->seekable) { + /* Fill in length fields */ + end_tag_be(pb, mmf->awapos); + end_tag_be(pb, mmf->atrpos); + end_tag_be(pb, 8); + + pos = avio_tell(pb); + size = pos - mmf->awapos; + + /* Fill Atsq chunk */ + avio_seek(pb, mmf->atsqpos, SEEK_SET); + + /* "play wav" */ + avio_w8(pb, 0); /* start time */ + avio_w8(pb, (mmf->stereo << 6) | 1); /* (channel << 6) | wavenum */ + gatetime = size * 500 / s->streams[0]->codec->sample_rate; + put_varlength(pb, gatetime); /* duration */ + + /* "nop" */ + put_varlength(pb, gatetime); /* start time */ + avio_write(pb, "\xff\x00", 2); /* nop */ + + /* "end of sequence" */ + avio_write(pb, "\x00\x00\x00\x00", 4); + + avio_seek(pb, pos, SEEK_SET); + + avio_flush(pb); + } + return 0; +} +#endif /* CONFIG_MMF_MUXER */ + +static int mmf_probe(AVProbeData *p) +{ + /* check file header */ + if (p->buf[0] == 'M' && p->buf[1] == 'M' && + p->buf[2] == 'M' && p->buf[3] == 'D' && + p->buf[8] == 'C' && p->buf[9] == 'N' && + p->buf[10] == 'T' && p->buf[11] == 'I') + return AVPROBE_SCORE_MAX; + else + return 0; +} + +/* mmf input */ +static int mmf_read_header(AVFormatContext *s) +{ + MMFContext *mmf = s->priv_data; + unsigned int tag; + AVIOContext *pb = s->pb; + AVStream *st; + int64_t size; + int rate, params; + + tag = avio_rl32(pb); + if (tag != MKTAG('M', 'M', 'M', 'D')) + return AVERROR_INVALIDDATA; + avio_skip(pb, 4); /* file_size */ + + /* Skip some unused chunks that may or may not be present */ + for(;; avio_skip(pb, size)) { + tag = avio_rl32(pb); + size = avio_rb32(pb); + if(tag == MKTAG('C','N','T','I')) continue; + if(tag == MKTAG('O','P','D','A')) continue; + break; + } + + /* Tag = "ATRx", where "x" = track number */ + if ((tag & 0xffffff) == MKTAG('M', 'T', 'R', 0)) { + av_log(s, AV_LOG_ERROR, "MIDI like format found, unsupported\n"); + return AVERROR_PATCHWELCOME; + } + if ((tag & 0xffffff) != MKTAG('A', 'T', 'R', 0)) { + av_log(s, AV_LOG_ERROR, "Unsupported SMAF chunk %08x\n", tag); + return AVERROR_PATCHWELCOME; + } + + avio_r8(pb); /* format type */ + avio_r8(pb); /* sequence type */ + params = avio_r8(pb); /* (channel << 7) | (format << 4) | rate */ + rate = mmf_rate(params & 0x0f); + if(rate < 0) { + av_log(s, AV_LOG_ERROR, "Invalid sample rate\n"); + return AVERROR_INVALIDDATA; + } + avio_r8(pb); /* wave base bit */ + avio_r8(pb); /* time base d */ + avio_r8(pb); /* time base g */ + + /* Skip some unused chunks that may or may not be present */ + for(;; avio_skip(pb, size)) { + tag = avio_rl32(pb); + size = avio_rb32(pb); + if(tag == MKTAG('A','t','s','q')) continue; + if(tag == MKTAG('A','s','p','I')) continue; + break; + } + + /* Make sure it's followed by an Awa chunk, aka wave data */ + if ((tag & 0xffffff) != MKTAG('A', 'w', 'a', 0)) { + av_log(s, AV_LOG_ERROR, "Unexpected SMAF chunk %08x\n", tag); + return AVERROR_INVALIDDATA; + } + mmf->data_end = avio_tell(pb) + size; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_YAMAHA; + st->codec->sample_rate = rate; + st->codec->channels = (params >> 7) + 1; + st->codec->channel_layout = params >> 7 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; + st->codec->bits_per_coded_sample = 4; + st->codec->bit_rate = st->codec->sample_rate * st->codec->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +#define MAX_SIZE 4096 + +static int mmf_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MMFContext *mmf = s->priv_data; + int64_t left, size; + int ret; + + left = mmf->data_end - avio_tell(s->pb); + size = FFMIN(left, MAX_SIZE); + if (url_feof(s->pb) || size <= 0) + return AVERROR_EOF; + + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + + return ret; +} + +#if CONFIG_MMF_DEMUXER +AVInputFormat ff_mmf_demuxer = { + .name = "mmf", + .long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), + .priv_data_size = sizeof(MMFContext), + .read_probe = mmf_probe, + .read_header = mmf_read_header, + .read_packet = mmf_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; +#endif +#if CONFIG_MMF_MUXER +AVOutputFormat ff_mmf_muxer = { + .name = "mmf", + .long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), + .mime_type = "application/vnd.smaf", + .extensions = "mmf", + .priv_data_size = sizeof(MMFContext), + .audio_codec = AV_CODEC_ID_ADPCM_YAMAHA, + .video_codec = AV_CODEC_ID_NONE, + .write_header = mmf_write_header, + .write_packet = ff_raw_write_packet, + .write_trailer = mmf_write_trailer, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/mmf.o libavformat/mmf.o: libavformat/mmf.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/pcm.h \ + libavformat/rawenc.h libavformat/riff.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmf.o Binary file ffmpeg/libavformat/mmf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mms.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mms.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,149 @@ +/* + * MMS protocol common definitions. + * Copyright (c) 2006,2007 Ryan Martell + * Copyright (c) 2007 Bjrn Axelsson + * Copyright (c) 2010 Zhentan Feng + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "mms.h" +#include "asf.h" +#include "libavutil/intreadwrite.h" + +#define MMS_MAX_STREAMS 256 /**< arbitrary sanity check value */ + +int ff_mms_read_header(MMSContext *mms, uint8_t *buf, const int size) +{ + char *pos; + int size_to_copy; + int remaining_size = mms->asf_header_size - mms->asf_header_read_size; + size_to_copy = FFMIN(size, remaining_size); + pos = mms->asf_header + mms->asf_header_read_size; + memcpy(buf, pos, size_to_copy); + if (mms->asf_header_read_size == mms->asf_header_size) { + av_freep(&mms->asf_header); // which contains asf header + } + mms->asf_header_read_size += size_to_copy; + return size_to_copy; +} + +int ff_mms_read_data(MMSContext *mms, uint8_t *buf, const int size) +{ + int read_size; + read_size = FFMIN(size, mms->remaining_in_len); + memcpy(buf, mms->read_in_ptr, read_size); + mms->remaining_in_len -= read_size; + mms->read_in_ptr += read_size; + return read_size; +} + +int ff_mms_asf_header_parser(MMSContext *mms) +{ + uint8_t *p = mms->asf_header; + uint8_t *end; + int flags, stream_id; + mms->stream_num = 0; + + if (mms->asf_header_size < sizeof(ff_asf_guid) * 2 + 22 || + memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (invalid ASF header, size=%d)\n", + mms->asf_header_size); + return AVERROR_INVALIDDATA; + } + + end = mms->asf_header + mms->asf_header_size; + + p += sizeof(ff_asf_guid) + 14; + while(end - p >= sizeof(ff_asf_guid) + 8) { + uint64_t chunksize; + if (!memcmp(p, ff_asf_data_header, sizeof(ff_asf_guid))) { + chunksize = 50; // see Reference [2] section 5.1 + } else { + chunksize = AV_RL64(p + sizeof(ff_asf_guid)); + } + if (!chunksize || chunksize > end - p) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (header chunksize %"PRId64" is invalid)\n", + chunksize); + return AVERROR_INVALIDDATA; + } + if (!memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { + /* read packet size */ + if (end - p > sizeof(ff_asf_guid) * 2 + 68) { + mms->asf_packet_len = AV_RL32(p + sizeof(ff_asf_guid) * 2 + 64); + if (mms->asf_packet_len <= 0 || mms->asf_packet_len > sizeof(mms->in_buffer)) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (too large pkt_len %d)\n", + mms->asf_packet_len); + return AVERROR_INVALIDDATA; + } + } + } else if (!memcmp(p, ff_asf_stream_header, sizeof(ff_asf_guid))) { + flags = AV_RL16(p + sizeof(ff_asf_guid)*3 + 24); + stream_id = flags & 0x7F; + //The second condition is for checking CS_PKT_STREAM_ID_REQUEST packet size, + //we can calcuate the packet size by stream_num. + //Please see function send_stream_selection_request(). + if (mms->stream_num < MMS_MAX_STREAMS && + 46 + mms->stream_num * 6 < sizeof(mms->out_buffer)) { + mms->streams = av_fast_realloc(mms->streams, + &mms->nb_streams_allocated, + (mms->stream_num + 1) * sizeof(MMSStream)); + mms->streams[mms->stream_num].id = stream_id; + mms->stream_num++; + } else { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (too many A/V streams)\n"); + return AVERROR_INVALIDDATA; + } + } else if (!memcmp(p, ff_asf_ext_stream_header, sizeof(ff_asf_guid))) { + if (end - p >= 88) { + int stream_count = AV_RL16(p + 84), ext_len_count = AV_RL16(p + 86); + uint64_t skip_bytes = 88; + while (stream_count--) { + if (end - p < skip_bytes + 4) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (next stream name length is not in the buffer)\n"); + return AVERROR_INVALIDDATA; + } + skip_bytes += 4 + AV_RL16(p + skip_bytes + 2); + } + while (ext_len_count--) { + if (end - p < skip_bytes + 22) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (next extension system info length is not in the buffer)\n"); + return AVERROR_INVALIDDATA; + } + skip_bytes += 22 + AV_RL32(p + skip_bytes + 18); + } + if (end - p < skip_bytes) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (the last extension system info length is invalid)\n"); + return AVERROR_INVALIDDATA; + } + if (chunksize - skip_bytes > 24) + chunksize = skip_bytes; + } + } else if (!memcmp(p, ff_asf_head1_guid, sizeof(ff_asf_guid))) { + chunksize = 46; // see references [2] section 3.4. This should be set 46. + } + p += chunksize; + } + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mms.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mms.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/mms.o libavformat/mms.o: libavformat/mms.c libavformat/mms.h \ + libavformat/url.h libavformat/avio.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/dict.h libavutil/log.h \ + libavformat/version.h libavutil/avutil.h libavformat/asf.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavformat/metadata.h libavformat/riff.h libavformat/internal.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mms.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mms.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,64 @@ +/* + * MMS protocol common definitions. + * Copyright (c) 2010 Zhentan Feng + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVFORMAT_MMS_H +#define AVFORMAT_MMS_H + +#include "url.h" + +typedef struct MMSStream { + int id; +}MMSStream; + +typedef struct MMSContext { + URLContext *mms_hd; ///< TCP connection handle + MMSStream *streams; + + /** Buffer for outgoing packets. */ + /*@{*/ + uint8_t *write_out_ptr; ///< Pointer for writing the buffer. + uint8_t out_buffer[512]; ///< Buffer for outgoing packet. + /*@}*/ + + /** Buffer for incoming packets. */ + /*@{*/ + uint8_t in_buffer[65536]; ///< Buffer for incoming packets. + uint8_t *read_in_ptr; ///< Pointer for reading from incoming buffer. + int remaining_in_len; ///< Reading length from incoming buffer. + /*@}*/ + + /** Internal handling of the ASF header */ + /*@{*/ + uint8_t *asf_header; ///< Stored ASF header. + int asf_header_size; ///< Size of stored ASF header. + int header_parsed; ///< The header has been received and parsed. + int asf_packet_len; + int asf_header_read_size; + /*@}*/ + + int stream_num; ///< stream numbers. + unsigned int nb_streams_allocated; ///< allocated size of streams +} MMSContext; + +int ff_mms_asf_header_parser(MMSContext * mms); +int ff_mms_read_data(MMSContext *mms, uint8_t *buf, const int size); +int ff_mms_read_header(MMSContext * mms, uint8_t * buf, const int size); + +#endif /* AVFORMAT_MMS_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mms.o Binary file ffmpeg/libavformat/mms.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmsh.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmsh.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,410 @@ +/* + * MMS protocol over HTTP + * Copyright (c) 2010 Zhentan Feng + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Reference + * Windows Media HTTP Streaming Protocol. + * http://msdn.microsoft.com/en-us/library/cc251059(PROT.10).aspx + */ + +#include +#include "libavutil/intreadwrite.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "mms.h" +#include "asf.h" +#include "http.h" +#include "url.h" + +#define CHUNK_HEADER_LENGTH 4 // 2bytes chunk type and 2bytes chunk length. +#define EXT_HEADER_LENGTH 8 // 4bytes sequence, 2bytes useless and 2bytes chunk length. + +// see Ref 2.2.1.8 +#define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n" +// see Ref 2.2.1.4.33 +// the guid value can be changed to any valid value. +#define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n" + +// see Ref 2.2.3 for packet type define: +// chunk type contains 2 fields: Frame and PacketID. +// Frame is 0x24 or 0xA4(rarely), different PacketID indicates different packet type. +typedef enum { + CHUNK_TYPE_DATA = 0x4424, + CHUNK_TYPE_ASF_HEADER = 0x4824, + CHUNK_TYPE_END = 0x4524, + CHUNK_TYPE_STREAM_CHANGE = 0x4324, +} ChunkType; + +typedef struct { + MMSContext mms; + uint8_t location[1024]; + int request_seq; ///< request packet sequence + int chunk_seq; ///< data packet sequence +} MMSHContext; + +static int mmsh_close(URLContext *h) +{ + MMSHContext *mmsh = (MMSHContext *)h->priv_data; + MMSContext *mms = &mmsh->mms; + if (mms->mms_hd) + ffurl_close(mms->mms_hd); + av_free(mms->streams); + av_free(mms->asf_header); + return 0; +} + +static ChunkType get_chunk_header(MMSHContext *mmsh, int *len) +{ + MMSContext *mms = &mmsh->mms; + uint8_t chunk_header[CHUNK_HEADER_LENGTH]; + uint8_t ext_header[EXT_HEADER_LENGTH]; + ChunkType chunk_type; + int chunk_len, res, ext_header_len; + + res = ffurl_read_complete(mms->mms_hd, chunk_header, CHUNK_HEADER_LENGTH); + if (res != CHUNK_HEADER_LENGTH) { + av_log(NULL, AV_LOG_ERROR, "Read data packet header failed!\n"); + return AVERROR(EIO); + } + chunk_type = AV_RL16(chunk_header); + chunk_len = AV_RL16(chunk_header + 2); + + switch (chunk_type) { + case CHUNK_TYPE_END: + case CHUNK_TYPE_STREAM_CHANGE: + ext_header_len = 4; + break; + case CHUNK_TYPE_ASF_HEADER: + case CHUNK_TYPE_DATA: + ext_header_len = 8; + break; + default: + av_log(NULL, AV_LOG_ERROR, "Strange chunk type %d\n", chunk_type); + return AVERROR_INVALIDDATA; + } + + res = ffurl_read_complete(mms->mms_hd, ext_header, ext_header_len); + if (res != ext_header_len) { + av_log(NULL, AV_LOG_ERROR, "Read ext header failed!\n"); + return AVERROR(EIO); + } + *len = chunk_len - ext_header_len; + if (chunk_type == CHUNK_TYPE_END || chunk_type == CHUNK_TYPE_DATA) + mmsh->chunk_seq = AV_RL32(ext_header); + return chunk_type; +} + +static int read_data_packet(MMSHContext *mmsh, const int len) +{ + MMSContext *mms = &mmsh->mms; + int res; + if (len > sizeof(mms->in_buffer)) { + av_log(NULL, AV_LOG_ERROR, + "Data packet length %d exceeds the in_buffer size %zu\n", + len, sizeof(mms->in_buffer)); + return AVERROR(EIO); + } + res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len); + av_dlog(NULL, "Data packet len = %d\n", len); + if (res != len) { + av_log(NULL, AV_LOG_ERROR, "Read data packet failed!\n"); + return AVERROR(EIO); + } + if (len > mms->asf_packet_len) { + av_log(NULL, AV_LOG_ERROR, + "Chunk length %d exceed packet length %d\n",len, mms->asf_packet_len); + return AVERROR_INVALIDDATA; + } else { + memset(mms->in_buffer + len, 0, mms->asf_packet_len - len); // padding + } + mms->read_in_ptr = mms->in_buffer; + mms->remaining_in_len = mms->asf_packet_len; + return 0; +} + +static int get_http_header_data(MMSHContext *mmsh) +{ + MMSContext *mms = &mmsh->mms; + int res, len; + ChunkType chunk_type; + + for (;;) { + len = 0; + res = chunk_type = get_chunk_header(mmsh, &len); + if (res < 0) { + return res; + } else if (chunk_type == CHUNK_TYPE_ASF_HEADER){ + // get asf header and stored it + if (!mms->header_parsed) { + if (mms->asf_header) { + if (len != mms->asf_header_size) { + mms->asf_header_size = len; + av_dlog(NULL, "Header len changed from %d to %d\n", + mms->asf_header_size, len); + av_freep(&mms->asf_header); + } + } + mms->asf_header = av_mallocz(len); + if (!mms->asf_header) { + return AVERROR(ENOMEM); + } + mms->asf_header_size = len; + } + if (len > mms->asf_header_size) { + av_log(NULL, AV_LOG_ERROR, + "Asf header packet len = %d exceed the asf header buf size %d\n", + len, mms->asf_header_size); + return AVERROR(EIO); + } + res = ffurl_read_complete(mms->mms_hd, mms->asf_header, len); + if (res != len) { + av_log(NULL, AV_LOG_ERROR, + "Recv asf header data len %d != expected len %d\n", res, len); + return AVERROR(EIO); + } + mms->asf_header_size = len; + if (!mms->header_parsed) { + res = ff_mms_asf_header_parser(mms); + mms->header_parsed = 1; + return res; + } + } else if (chunk_type == CHUNK_TYPE_DATA) { + // read data packet and do padding + return read_data_packet(mmsh, len); + } else { + if (len) { + if (len > sizeof(mms->in_buffer)) { + av_log(NULL, AV_LOG_ERROR, + "Other packet len = %d exceed the in_buffer size %zu\n", + len, sizeof(mms->in_buffer)); + return AVERROR(EIO); + } + res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len); + if (res != len) { + av_log(NULL, AV_LOG_ERROR, "Read other chunk type data failed!\n"); + return AVERROR(EIO); + } else { + av_dlog(NULL, "Skip chunk type %d \n", chunk_type); + continue; + } + } + } + } +} + +static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos) +{ + int i, port, err; + char httpname[256], path[256], host[128]; + char *stream_selection = NULL; + char headers[1024]; + MMSHContext *mmsh = h->priv_data; + MMSContext *mms; + + mmsh->request_seq = h->is_streamed = 1; + mms = &mmsh->mms; + av_strlcpy(mmsh->location, uri, sizeof(mmsh->location)); + + av_url_split(NULL, 0, NULL, 0, + host, sizeof(host), &port, path, sizeof(path), mmsh->location); + if (port<0) + port = 80; // default mmsh protocol port + ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path); + + if (ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ, + &h->interrupt_callback) < 0) { + return AVERROR(EIO); + } + + snprintf(headers, sizeof(headers), + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Pragma: no-cache,rate=1.000000,stream-time=0," + "stream-offset=0:0,request-context=%u,max-duration=0\r\n" + CLIENTGUID + "Connection: Close\r\n", + host, port, mmsh->request_seq++); + av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0); + + err = ffurl_connect(mms->mms_hd, NULL); + if (err) { + goto fail; + } + err = get_http_header_data(mmsh); + if (err) { + av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n"); + goto fail; + } + + // close the socket and then reopen it for sending the second play request. + ffurl_close(mms->mms_hd); + memset(headers, 0, sizeof(headers)); + if ((err = ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ, + &h->interrupt_callback)) < 0) { + goto fail; + } + stream_selection = av_mallocz(mms->stream_num * 19 + 1); + if (!stream_selection) + return AVERROR(ENOMEM); + for (i = 0; i < mms->stream_num; i++) { + char tmp[20]; + err = snprintf(tmp, sizeof(tmp), "ffff:%d:0 ", mms->streams[i].id); + if (err < 0) + goto fail; + av_strlcat(stream_selection, tmp, mms->stream_num * 19 + 1); + } + // send play request + err = snprintf(headers, sizeof(headers), + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Pragma: no-cache,rate=1.000000,request-context=%u\r\n" + "Pragma: xPlayStrm=1\r\n" + CLIENTGUID + "Pragma: stream-switch-count=%d\r\n" + "Pragma: stream-switch-entry=%s\r\n" + "Pragma: no-cache,rate=1.000000,stream-time=%u" + "Connection: Close\r\n", + host, port, mmsh->request_seq++, mms->stream_num, stream_selection, timestamp); + av_freep(&stream_selection); + if (err < 0) { + av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n"); + goto fail; + } + av_dlog(NULL, "out_buffer is %s", headers); + av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0); + + err = ffurl_connect(mms->mms_hd, NULL); + if (err) { + goto fail; + } + + err = get_http_header_data(mmsh); + if (err) { + av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n"); + goto fail; + } + + av_dlog(NULL, "Connection successfully open\n"); + return 0; +fail: + av_freep(&stream_selection); + mmsh_close(h); + av_dlog(NULL, "Connection failed with error %d\n", err); + return err; +} + +static int mmsh_open(URLContext *h, const char *uri, int flags) +{ + return mmsh_open_internal(h, uri, flags, 0, 0); +} + +static int handle_chunk_type(MMSHContext *mmsh) +{ + MMSContext *mms = &mmsh->mms; + int res, len = 0; + ChunkType chunk_type; + chunk_type = get_chunk_header(mmsh, &len); + + switch (chunk_type) { + case CHUNK_TYPE_END: + mmsh->chunk_seq = 0; + av_log(NULL, AV_LOG_ERROR, "Stream ended!\n"); + return AVERROR(EIO); + case CHUNK_TYPE_STREAM_CHANGE: + mms->header_parsed = 0; + if (res = get_http_header_data(mmsh)) { + av_log(NULL, AV_LOG_ERROR,"Stream changed! Failed to get new header!\n"); + return res; + } + break; + case CHUNK_TYPE_DATA: + return read_data_packet(mmsh, len); + default: + av_log(NULL, AV_LOG_ERROR, "Recv other type packet %d\n", chunk_type); + return AVERROR_INVALIDDATA; + } + return 0; +} + +static int mmsh_read(URLContext *h, uint8_t *buf, int size) +{ + int res = 0; + MMSHContext *mmsh = h->priv_data; + MMSContext *mms = &mmsh->mms; + do { + if (mms->asf_header_read_size < mms->asf_header_size) { + // copy asf header into buffer + res = ff_mms_read_header(mms, buf, size); + } else { + if (!mms->remaining_in_len && (res = handle_chunk_type(mmsh))) + return res; + res = ff_mms_read_data(mms, buf, size); + } + } while (!res); + return res; +} + +static int64_t mmsh_read_seek(URLContext *h, int stream_index, + int64_t timestamp, int flags) +{ + MMSHContext *mmsh = h->priv_data; + MMSContext *mms = &mmsh->mms; + int ret; + + ret= mmsh_open_internal(h, mmsh->location, 0, FFMAX(timestamp, 0), 0); + + if(ret>=0){ + if (mms->mms_hd) + ffurl_close(mms->mms_hd); + av_freep(&mms->streams); + av_freep(&mms->asf_header); + av_free(mmsh); + mmsh = h->priv_data; + mms = &mmsh->mms; + mms->asf_header_read_size= mms->asf_header_size; + }else + h->priv_data= mmsh; + return ret; +} + +static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence) +{ + MMSHContext *mmsh = h->priv_data; + MMSContext *mms = &mmsh->mms; + + if(pos == 0 && whence == SEEK_CUR) + return mms->asf_header_read_size + mms->remaining_in_len + mmsh->chunk_seq * (int64_t)mms->asf_packet_len; + return AVERROR(ENOSYS); +} + +URLProtocol ff_mmsh_protocol = { + .name = "mmsh", + .url_open = mmsh_open, + .url_read = mmsh_read, + .url_seek = mmsh_seek, + .url_close = mmsh_close, + .url_read_seek = mmsh_read_seek, + .priv_data_size = sizeof(MMSHContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmsh.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmsh.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mmsh.o libavformat/mmsh.o: libavformat/mmsh.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/avstring.h libavutil/opt.h libavutil/rational.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavformat/internal.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/mms.h \ + libavformat/url.h libavformat/asf.h libavformat/metadata.h \ + libavformat/riff.h libavformat/http.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmsh.o Binary file ffmpeg/libavformat/mmsh.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmst.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmst.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,629 @@ +/* + * MMS protocol over TCP + * Copyright (c) 2006,2007 Ryan Martell + * Copyright (c) 2007 Bjrn Axelsson + * Copyright (c) 2010 Zhentan Feng + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* References + * MMS protocol specification: + * [1]http://msdn.microsoft.com/en-us/library/cc234711(PROT.10).aspx + * ASF specification. Revision 01.20.03. + * [2]http://msdn.microsoft.com/en-us/library/bb643323.aspx + */ + +#include "avformat.h" +#include "mms.h" +#include "internal.h" +#include "avio_internal.h" +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "network.h" +#include "url.h" + +#define LOCAL_ADDRESS 0xc0a80081 // FIXME get and use correct local ip address. +#define LOCAL_PORT 1037 // as above. +/** Client to server packet types. */ +typedef enum { + CS_PKT_INITIAL = 0x01, + CS_PKT_PROTOCOL_SELECT = 0x02, + CS_PKT_MEDIA_FILE_REQUEST = 0x05, + CS_PKT_START_FROM_PKT_ID = 0x07, + CS_PKT_STREAM_PAUSE = 0x09, + CS_PKT_STREAM_CLOSE = 0x0d, + CS_PKT_MEDIA_HEADER_REQUEST = 0x15, + CS_PKT_TIMING_DATA_REQUEST = 0x18, + CS_PKT_USER_PASSWORD = 0x1a, + CS_PKT_KEEPALIVE = 0x1b, + CS_PKT_STREAM_ID_REQUEST = 0x33, +} MMSCSPacketType; + +/** Server to client packet types. */ +typedef enum { + /** Control packets. */ + /*@{*/ + SC_PKT_CLIENT_ACCEPTED = 0x01, + SC_PKT_PROTOCOL_ACCEPTED = 0x02, + SC_PKT_PROTOCOL_FAILED = 0x03, + SC_PKT_MEDIA_PKT_FOLLOWS = 0x05, + SC_PKT_MEDIA_FILE_DETAILS = 0x06, + SC_PKT_HEADER_REQUEST_ACCEPTED = 0x11, + SC_PKT_TIMING_TEST_REPLY = 0x15, + SC_PKT_PASSWORD_REQUIRED = 0x1a, + SC_PKT_KEEPALIVE = 0x1b, + SC_PKT_STREAM_STOPPED = 0x1e, + SC_PKT_STREAM_CHANGING = 0x20, + SC_PKT_STREAM_ID_ACCEPTED = 0x21, + /*@}*/ + + /** Pseudo packets. */ + /*@{*/ + SC_PKT_CANCEL = -1, + SC_PKT_NO_DATA = -2, + /*@}*/ + + /** Data packets. */ + /*@{*/ + SC_PKT_ASF_HEADER = 0x010000,// make it bigger than 0xFF in case of + SC_PKT_ASF_MEDIA = 0x010001,// receiving false data packets. + /*@}*/ +} MMSSCPacketType; + +typedef struct { + MMSContext mms; + int outgoing_packet_seq; ///< Outgoing packet sequence number. + char path[256]; ///< Path of the resource being asked for. + char host[128]; ///< Host of the resources. + int incoming_packet_seq; ///< Incoming packet sequence number. + int incoming_flags; ///< Incoming packet flags. + int packet_id; ///< Identifier for packets in the current stream. + unsigned int header_packet_id; ///< default is 2. +} MMSTContext; + +/** Create MMST command packet header */ +static void start_command_packet(MMSTContext *mmst, MMSCSPacketType packet_type) +{ + MMSContext *mms = &mmst->mms; + mms->write_out_ptr = mms->out_buffer; + + bytestream_put_le32(&mms->write_out_ptr, 1); // start sequence + bytestream_put_le32(&mms->write_out_ptr, 0xb00bface); + bytestream_put_le32(&mms->write_out_ptr, 0); // Length starts from after the protocol type bytes + bytestream_put_le32(&mms->write_out_ptr, MKTAG('M','M','S',' ')); + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, mmst->outgoing_packet_seq++); + bytestream_put_le64(&mms->write_out_ptr, 0); // timestamp + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le16(&mms->write_out_ptr, packet_type); + bytestream_put_le16(&mms->write_out_ptr, 3); // direction to server +} + +/** Add prefixes to MMST command packet. */ +static void insert_command_prefixes(MMSContext *mms, + uint32_t prefix1, uint32_t prefix2) +{ + bytestream_put_le32(&mms->write_out_ptr, prefix1); // first prefix + bytestream_put_le32(&mms->write_out_ptr, prefix2); // second prefix +} + +/** Send a prepared MMST command packet. */ +static int send_command_packet(MMSTContext *mmst) +{ + MMSContext *mms = &mmst->mms; + int len= mms->write_out_ptr - mms->out_buffer; + int exact_length = FFALIGN(len, 8); + int first_length= exact_length - 16; + int len8= first_length/8; + int write_result; + + // update packet length fields. + AV_WL32(mms->out_buffer + 8, first_length); + AV_WL32(mms->out_buffer + 16, len8); + AV_WL32(mms->out_buffer + 32, len8-2); + memset(mms->write_out_ptr, 0, exact_length - len); + + // write it out. + write_result= ffurl_write(mms->mms_hd, mms->out_buffer, exact_length); + if(write_result != exact_length) { + av_log(NULL, AV_LOG_ERROR, + "Failed to write data of length %d: %d (%s)\n", + exact_length, write_result, + write_result < 0 ? strerror(AVUNERROR(write_result)) : + "The server closed the connection"); + return AVERROR(EIO); + } + + return 0; +} + +static void mms_put_utf16(MMSContext *mms, const uint8_t *src) +{ + AVIOContext bic; + int size = mms->write_out_ptr - mms->out_buffer; + int len; + ffio_init_context(&bic, mms->write_out_ptr, + sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL); + + len = avio_put_str16le(&bic, src); + mms->write_out_ptr += len; +} + +static int send_time_test_data(MMSTContext *mmst) +{ + start_command_packet(mmst, CS_PKT_TIMING_DATA_REQUEST); + insert_command_prefixes(&mmst->mms, 0x00f0f0f0, 0x0004000b); + return send_command_packet(mmst); +} + +static int send_protocol_select(MMSTContext *mmst) +{ + char data_string[256]; + MMSContext *mms = &mmst->mms; + + start_command_packet(mmst, CS_PKT_PROTOCOL_SELECT); + insert_command_prefixes(mms, 0, 0xffffffff); + bytestream_put_le32(&mms->write_out_ptr, 0); // maxFunnelBytes + bytestream_put_le32(&mms->write_out_ptr, 0x00989680); // maxbitRate + bytestream_put_le32(&mms->write_out_ptr, 2); // funnelMode + snprintf(data_string, sizeof(data_string), "\\\\%d.%d.%d.%d\\%s\\%d", + (LOCAL_ADDRESS>>24)&0xff, + (LOCAL_ADDRESS>>16)&0xff, + (LOCAL_ADDRESS>>8)&0xff, + LOCAL_ADDRESS&0xff, + "TCP", // or UDP + LOCAL_PORT); + + mms_put_utf16(mms, data_string); + return send_command_packet(mmst); +} + +static int send_media_file_request(MMSTContext *mmst) +{ + MMSContext *mms = &mmst->mms; + start_command_packet(mmst, CS_PKT_MEDIA_FILE_REQUEST); + insert_command_prefixes(mms, 1, 0xffffffff); + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, 0); + mms_put_utf16(mms, mmst->path + 1); // +1 for skip "/" + + return send_command_packet(mmst); +} + +static void handle_packet_stream_changing_type(MMSTContext *mmst) +{ + MMSContext *mms = &mmst->mms; + av_dlog(NULL, "Stream changing!\n"); + + // 40 is the packet header size, 7 is the prefix size. + mmst->header_packet_id= AV_RL32(mms->in_buffer + 40 + 7); + av_dlog(NULL, "Changed header prefix to 0x%x", mmst->header_packet_id); +} + +static int send_keepalive_packet(MMSTContext *mmst) +{ + // respond to a keepalive with a keepalive... + start_command_packet(mmst, CS_PKT_KEEPALIVE); + insert_command_prefixes(&mmst->mms, 1, 0x100FFFF); + return send_command_packet(mmst); +} + +/** Pad media packets smaller than max_packet_size and/or adjust read position + * after a seek. */ +static void pad_media_packet(MMSContext *mms) +{ + if(mms->remaining_in_lenasf_packet_len) { + int padding_size = mms->asf_packet_len - mms->remaining_in_len; + memset(mms->in_buffer + mms->remaining_in_len, 0, padding_size); + mms->remaining_in_len += padding_size; + } +} + +/** Read incoming MMST media, header or command packet. */ +static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) +{ + int read_result; + MMSSCPacketType packet_type= -1; + MMSContext *mms = &mmst->mms; + for(;;) { + read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer, 8); + if (read_result != 8) { + if(read_result < 0) { + av_log(NULL, AV_LOG_ERROR, + "Error reading packet header: %d (%s)\n", + read_result, strerror(AVUNERROR(read_result))); + packet_type = SC_PKT_CANCEL; + } else { + av_log(NULL, AV_LOG_ERROR, + "The server closed the connection\n"); + packet_type = SC_PKT_NO_DATA; + } + return packet_type; + } + + // handle command packet. + if(AV_RL32(mms->in_buffer + 4)==0xb00bface) { + int length_remaining, hr; + + mmst->incoming_flags= mms->in_buffer[3]; + read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer+8, 4); + if(read_result != 4) { + av_log(NULL, AV_LOG_ERROR, + "Reading command packet length failed: %d (%s)\n", + read_result, + read_result < 0 ? strerror(AVUNERROR(read_result)) : + "The server closed the connection"); + return read_result < 0 ? read_result : AVERROR(EIO); + } + + length_remaining= AV_RL32(mms->in_buffer+8) + 4; + av_dlog(NULL, "Length remaining is %d\n", length_remaining); + // read the rest of the packet. + if (length_remaining < 0 + || length_remaining > sizeof(mms->in_buffer) - 12) { + av_log(NULL, AV_LOG_ERROR, + "Incoming packet length %d exceeds bufsize %zu\n", + length_remaining, sizeof(mms->in_buffer) - 12); + return AVERROR_INVALIDDATA; + } + read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer + 12, + length_remaining) ; + if (read_result != length_remaining) { + av_log(NULL, AV_LOG_ERROR, + "Reading pkt data (length=%d) failed: %d (%s)\n", + length_remaining, read_result, + read_result < 0 ? strerror(AVUNERROR(read_result)) : + "The server closed the connection"); + return read_result < 0 ? read_result : AVERROR(EIO); + } + packet_type= AV_RL16(mms->in_buffer+36); + if (read_result >= 44 && (hr = AV_RL32(mms->in_buffer + 40))) { + av_log(NULL, AV_LOG_ERROR, + "Server sent a message with packet type 0x%x and error status code 0x%08x\n", packet_type, hr); + return AVERROR(EINVAL); + } + } else { + int length_remaining; + int packet_id_type; + int tmp; + + // note we cache the first 8 bytes, + // then fill up the buffer with the others + tmp = AV_RL16(mms->in_buffer + 6); + length_remaining = (tmp - 8) & 0xffff; + mmst->incoming_packet_seq = AV_RL32(mms->in_buffer); + packet_id_type = mms->in_buffer[4]; + mmst->incoming_flags = mms->in_buffer[5]; + + if (length_remaining < 0 + || length_remaining > sizeof(mms->in_buffer) - 8) { + av_log(NULL, AV_LOG_ERROR, + "Data length %d is invalid or too large (max=%zu)\n", + length_remaining, sizeof(mms->in_buffer)); + return AVERROR_INVALIDDATA; + } + mms->remaining_in_len = length_remaining; + mms->read_in_ptr = mms->in_buffer; + read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer, length_remaining); + if(read_result != length_remaining) { + av_log(NULL, AV_LOG_ERROR, + "Failed to read packet data of size %d: %d (%s)\n", + length_remaining, read_result, + read_result < 0 ? strerror(AVUNERROR(read_result)) : + "The server closed the connection"); + return read_result < 0 ? read_result : AVERROR(EIO); + } + + // if we successfully read everything. + if(packet_id_type == mmst->header_packet_id) { + packet_type = SC_PKT_ASF_HEADER; + // Store the asf header + if(!mms->header_parsed) { + void *p = av_realloc(mms->asf_header, + mms->asf_header_size + mms->remaining_in_len); + if (!p) { + av_freep(&mms->asf_header); + return AVERROR(ENOMEM); + } + mms->asf_header = p; + memcpy(mms->asf_header + mms->asf_header_size, + mms->read_in_ptr, mms->remaining_in_len); + mms->asf_header_size += mms->remaining_in_len; + } + // 0x04 means asf header is sent in multiple packets. + if (mmst->incoming_flags == 0x04) + continue; + } else if(packet_id_type == mmst->packet_id) { + packet_type = SC_PKT_ASF_MEDIA; + } else { + av_dlog(NULL, "packet id type %d is old.", packet_id_type); + continue; + } + } + + // preprocess some packet type + if(packet_type == SC_PKT_KEEPALIVE) { + send_keepalive_packet(mmst); + continue; + } else if(packet_type == SC_PKT_STREAM_CHANGING) { + handle_packet_stream_changing_type(mmst); + } else if(packet_type == SC_PKT_ASF_MEDIA) { + pad_media_packet(mms); + } + return packet_type; + } +} + +static int mms_safe_send_recv(MMSTContext *mmst, + int (*send_fun)(MMSTContext *mmst), + const MMSSCPacketType expect_type) +{ + MMSSCPacketType type; + if(send_fun) { + int ret = send_fun(mmst); + if (ret < 0) { + av_dlog(NULL, "Send Packet error before expecting recv packet %d\n", expect_type); + return ret; + } + } + + if ((type = get_tcp_server_response(mmst)) != expect_type) { + av_log(NULL, AV_LOG_ERROR, + "Corrupt stream (unexpected packet type 0x%x, expected 0x%x)\n", + type, expect_type); + return AVERROR_INVALIDDATA; + } else { + return 0; + } +} + +static int send_media_header_request(MMSTContext *mmst) +{ + MMSContext *mms = &mmst->mms; + start_command_packet(mmst, CS_PKT_MEDIA_HEADER_REQUEST); + insert_command_prefixes(mms, 1, 0); + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, 0x00800000); + bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, 0); + + // the media preroll value in milliseconds? + bytestream_put_le32(&mms->write_out_ptr, 0); + bytestream_put_le32(&mms->write_out_ptr, 0x40AC2000); + bytestream_put_le32(&mms->write_out_ptr, 2); + bytestream_put_le32(&mms->write_out_ptr, 0); + + return send_command_packet(mmst); +} + +/** Send the initial handshake. */ +static int send_startup_packet(MMSTContext *mmst) +{ + char data_string[256]; + MMSContext *mms = &mmst->mms; + // SubscriberName is defined in MS specification linked below. + // The guid value can be any valid value. + // http://download.microsoft.com/ + // download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-WMSP%5D.pdf + snprintf(data_string, sizeof(data_string), + "NSPlayer/7.0.0.1956; {%s}; Host: %s", + "7E667F5D-A661-495E-A512-F55686DDA178", mmst->host); + + start_command_packet(mmst, CS_PKT_INITIAL); + insert_command_prefixes(mms, 0, 0x0004000b); + bytestream_put_le32(&mms->write_out_ptr, 0x0003001c); + mms_put_utf16(mms, data_string); + return send_command_packet(mmst); +} + +/** Send MMST stream selection command based on the AVStream->discard values. */ +static int send_stream_selection_request(MMSTContext *mmst) +{ + int i; + MMSContext *mms = &mmst->mms; + // send the streams we want back... + start_command_packet(mmst, CS_PKT_STREAM_ID_REQUEST); + bytestream_put_le32(&mms->write_out_ptr, mms->stream_num); // stream nums + for(i= 0; istream_num; i++) { + bytestream_put_le16(&mms->write_out_ptr, 0xffff); // flags + bytestream_put_le16(&mms->write_out_ptr, mms->streams[i].id); // stream id + bytestream_put_le16(&mms->write_out_ptr, 0); // selection + } + return send_command_packet(mmst); +} + +static int send_close_packet(MMSTContext *mmst) +{ + start_command_packet(mmst, CS_PKT_STREAM_CLOSE); + insert_command_prefixes(&mmst->mms, 1, 1); + + return send_command_packet(mmst); +} + +/** Close the MMSH/MMST connection */ +static int mms_close(URLContext *h) +{ + MMSTContext *mmst = (MMSTContext *)h->priv_data; + MMSContext *mms = &mmst->mms; + if(mms->mms_hd) { + send_close_packet(mmst); + ffurl_close(mms->mms_hd); + } + + /* free all separately allocated pointers in mms */ + av_free(mms->streams); + av_free(mms->asf_header); + + return 0; +} + +static int send_media_packet_request(MMSTContext *mmst) +{ + MMSContext *mms = &mmst->mms; + start_command_packet(mmst, CS_PKT_START_FROM_PKT_ID); + insert_command_prefixes(mms, 1, 0x0001FFFF); + bytestream_put_le64(&mms->write_out_ptr, 0); // seek timestamp + bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // unknown + bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // packet offset + bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit + bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit + bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit + bytestream_put_byte(&mms->write_out_ptr, 0x00); // stream time limit flag + + mmst->packet_id++; // new packet_id + bytestream_put_le32(&mms->write_out_ptr, mmst->packet_id); + return send_command_packet(mmst); +} + + +static void clear_stream_buffers(MMSContext *mms) +{ + mms->remaining_in_len = 0; + mms->read_in_ptr = mms->in_buffer; +} + +static int mms_open(URLContext *h, const char *uri, int flags) +{ + MMSTContext *mmst = h->priv_data; + MMSContext *mms; + int port, err; + char tcpname[256]; + + h->is_streamed = 1; + mms = &mmst->mms; + + // only for MMS over TCP, so set proto = NULL + av_url_split(NULL, 0, NULL, 0, + mmst->host, sizeof(mmst->host), &port, mmst->path, + sizeof(mmst->path), uri); + + if(port<0) + port = 1755; // defaut mms protocol port + + // establish tcp connection. + ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL); + err = ffurl_open(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, NULL); + if (err) + goto fail; + + mmst->packet_id = 3; // default, initial value. + mmst->header_packet_id = 2; // default, initial value. + err = mms_safe_send_recv(mmst, send_startup_packet, SC_PKT_CLIENT_ACCEPTED); + if (err) + goto fail; + err = mms_safe_send_recv(mmst, send_time_test_data, SC_PKT_TIMING_TEST_REPLY); + if (err) + goto fail; + err = mms_safe_send_recv(mmst, send_protocol_select, SC_PKT_PROTOCOL_ACCEPTED); + if (err) + goto fail; + err = mms_safe_send_recv(mmst, send_media_file_request, SC_PKT_MEDIA_FILE_DETAILS); + if (err) + goto fail; + err = mms_safe_send_recv(mmst, send_media_header_request, SC_PKT_HEADER_REQUEST_ACCEPTED); + if (err) + goto fail; + err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_HEADER); + if (err) + goto fail; + if((mmst->incoming_flags != 0X08) && (mmst->incoming_flags != 0X0C)) { + av_log(NULL, AV_LOG_ERROR, + "The server does not support MMST (try MMSH or RTSP)\n"); + err = AVERROR(EINVAL); + goto fail; + } + err = ff_mms_asf_header_parser(mms); + if (err) { + av_dlog(NULL, "asf header parsed failed!\n"); + goto fail; + } + mms->header_parsed = 1; + + if (!mms->asf_packet_len || !mms->stream_num) + goto fail; + + clear_stream_buffers(mms); + err = mms_safe_send_recv(mmst, send_stream_selection_request, SC_PKT_STREAM_ID_ACCEPTED); + if (err) + goto fail; + // send media packet request + err = mms_safe_send_recv(mmst, send_media_packet_request, SC_PKT_MEDIA_PKT_FOLLOWS); + if (err) { + goto fail; + } + av_dlog(NULL, "Leaving open (success)\n"); + return 0; +fail: + mms_close(h); + av_dlog(NULL, "Leaving open (failure: %d)\n", err); + return err; +} + +/** Read ASF data through the protocol. */ +static int mms_read(URLContext *h, uint8_t *buf, int size) +{ + /* TODO: see tcp.c:tcp_read() about a possible timeout scheme */ + MMSTContext *mmst = h->priv_data; + MMSContext *mms = &mmst->mms; + int result = 0; + + do { + if(mms->asf_header_read_size < mms->asf_header_size) { + /* Read from ASF header buffer */ + result = ff_mms_read_header(mms, buf, size); + } else if(mms->remaining_in_len) { + /* Read remaining packet data to buffer. + * the result can not be zero because remaining_in_len is positive.*/ + result = ff_mms_read_data(mms, buf, size); + } else { + /* Read from network */ + int err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_MEDIA); + if (err == 0) { + if(mms->remaining_in_len>mms->asf_packet_len) { + av_log(NULL, AV_LOG_ERROR, + "Incoming pktlen %d is larger than ASF pktsize %d\n", + mms->remaining_in_len, mms->asf_packet_len); + result= AVERROR(EIO); + } else { + // copy the data to the packet buffer. + result = ff_mms_read_data(mms, buf, size); + if (result == 0) { + av_dlog(NULL, "Read ASF media packet size is zero!\n"); + break; + } + } + } else { + av_dlog(NULL, "read packet error!\n"); + break; + } + } + } while(!result); // only return one packet. + return result; +} + +URLProtocol ff_mmst_protocol = { + .name = "mmst", + .url_open = mms_open, + .url_read = mms_read, + .url_close = mms_close, + .priv_data_size = sizeof(MMSTContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmst.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mmst.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/mmst.o libavformat/mmst.o: libavformat/mmst.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/mms.h \ + libavformat/url.h libavformat/internal.h libavformat/avio_internal.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavcodec/bytestream.h \ + libavutil/intreadwrite.h libavformat/network.h config.h \ + libavutil/error.h libavformat/os_support.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mmst.o Binary file ffmpeg/libavformat/mmst.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mov.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,3408 @@ +/* + * MOV demuxer + * Copyright (c) 2001 Fabrice Bellard + * Copyright (c) 2009 Baptiste Coudurier + * + * first version by Francois Revol + * seek function by Gael Chardon + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +//#define DEBUG +//#define MOV_EXPORT_ALL_METADATA + +#include "libavutil/attributes.h" +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "libavutil/mathematics.h" +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/opt.h" +#include "libavutil/timecode.h" +#include "libavcodec/ac3tab.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "riff.h" +#include "isom.h" +#include "libavcodec/get_bits.h" +#include "id3v1.h" +#include "mov_chan.h" + +#if CONFIG_ZLIB +#include +#endif + +#include "qtpalette.h" + + +#undef NDEBUG +#include + +/* those functions parse an atom */ +/* links atom IDs to parse functions */ +typedef struct MOVParseTableEntry { + uint32_t type; + int (*parse)(MOVContext *ctx, AVIOContext *pb, MOVAtom atom); +} MOVParseTableEntry; + +static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom); + +static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + short current, total = 0; + avio_rb16(pb); // unknown + current = avio_rb16(pb); + if (len >= 6) + total = avio_rb16(pb); + if (!total) + snprintf(buf, sizeof(buf), "%d", current); + else + snprintf(buf, sizeof(buf), "%d/%d", current, total); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_int8_bypass_padding(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + /* bypass padding bytes */ + avio_r8(pb); + avio_r8(pb); + avio_r8(pb); + + snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_int8_no_padding(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", avio_r8(pb)); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_metadata_gnre(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + short genre; + char buf[20]; + + avio_r8(pb); // unknown + + genre = avio_r8(pb); + if (genre < 1 || genre > ID3v1_GENRE_MAX) + return 0; + snprintf(buf, sizeof(buf), "%s", ff_id3v1_genre_str[genre-1]); + av_dict_set(&c->fc->metadata, key, buf, 0); + + return 0; +} + +static int mov_read_custom_metadata(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + char key[1024]={0}, data[1024]={0}; + int i; + AVStream *st; + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + if (atom.size <= 8) return 0; + + for (i = 0; i < 3; i++) { // Parse up to three sub-atoms looking for name and data. + int data_size = avio_rb32(pb); + int tag = avio_rl32(pb); + int str_size = 0, skip_size = 0; + char *target = NULL; + + switch (tag) { + case MKTAG('n','a','m','e'): + avio_rb32(pb); // version/flags + str_size = skip_size = data_size - 12; + atom.size -= 12; + target = key; + break; + case MKTAG('d','a','t','a'): + avio_rb32(pb); // version/flags + avio_rb32(pb); // reserved (zero) + str_size = skip_size = data_size - 16; + atom.size -= 16; + target = data; + break; + default: + skip_size = data_size - 8; + str_size = 0; + break; + } + + if (target) { + str_size = FFMIN3(sizeof(data)-1, str_size, atom.size); + avio_read(pb, target, str_size); + target[str_size] = 0; + } + atom.size -= skip_size; + + // If we didn't read the full data chunk for the sub-atom, skip to the end of it. + if (skip_size > str_size) avio_skip(pb, skip_size - str_size); + } + + if (*key && *data) { + if (strcmp(key, "iTunSMPB") == 0) { + int priming, remainder, samples; + if(sscanf(data, "%*X %X %X %X", &priming, &remainder, &samples) == 3){ + if(priming>0 && priming<16384) + sc->start_pad = priming; + return 1; + } + } + if (strcmp(key, "cdec") == 0) { +// av_dict_set(&st->metadata, key, data, 0); + return 1; + } + } + return 0; +} + +static const uint32_t mac_to_unicode[128] = { + 0x00C4,0x00C5,0x00C7,0x00C9,0x00D1,0x00D6,0x00DC,0x00E1, + 0x00E0,0x00E2,0x00E4,0x00E3,0x00E5,0x00E7,0x00E9,0x00E8, + 0x00EA,0x00EB,0x00ED,0x00EC,0x00EE,0x00EF,0x00F1,0x00F3, + 0x00F2,0x00F4,0x00F6,0x00F5,0x00FA,0x00F9,0x00FB,0x00FC, + 0x2020,0x00B0,0x00A2,0x00A3,0x00A7,0x2022,0x00B6,0x00DF, + 0x00AE,0x00A9,0x2122,0x00B4,0x00A8,0x2260,0x00C6,0x00D8, + 0x221E,0x00B1,0x2264,0x2265,0x00A5,0x00B5,0x2202,0x2211, + 0x220F,0x03C0,0x222B,0x00AA,0x00BA,0x03A9,0x00E6,0x00F8, + 0x00BF,0x00A1,0x00AC,0x221A,0x0192,0x2248,0x2206,0x00AB, + 0x00BB,0x2026,0x00A0,0x00C0,0x00C3,0x00D5,0x0152,0x0153, + 0x2013,0x2014,0x201C,0x201D,0x2018,0x2019,0x00F7,0x25CA, + 0x00FF,0x0178,0x2044,0x20AC,0x2039,0x203A,0xFB01,0xFB02, + 0x2021,0x00B7,0x201A,0x201E,0x2030,0x00C2,0x00CA,0x00C1, + 0x00CB,0x00C8,0x00CD,0x00CE,0x00CF,0x00CC,0x00D3,0x00D4, + 0xF8FF,0x00D2,0x00DA,0x00DB,0x00D9,0x0131,0x02C6,0x02DC, + 0x00AF,0x02D8,0x02D9,0x02DA,0x00B8,0x02DD,0x02DB,0x02C7, +}; + +static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len, + char *dst, int dstlen) +{ + char *p = dst; + char *end = dst+dstlen-1; + int i; + + for (i = 0; i < len; i++) { + uint8_t t, c = avio_r8(pb); + if (c < 0x80 && p < end) + *p++ = c; + else if (p < end) + PUT_UTF8(mac_to_unicode[c-0x80], t, if (p < end) *p++ = t;); + } + *p = 0; + return p - dst; +} + +static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) +{ + AVPacket pkt; + AVStream *st; + MOVStreamContext *sc; + enum AVCodecID id; + int ret; + + switch (type) { + case 0xd: id = AV_CODEC_ID_MJPEG; break; + case 0xe: id = AV_CODEC_ID_PNG; break; + case 0x1b: id = AV_CODEC_ID_BMP; break; + default: + av_log(c->fc, AV_LOG_WARNING, "Unknown cover type: 0x%x.\n", type); + avio_skip(pb, len); + return 0; + } + + st = avformat_new_stream(c->fc, NULL); + if (!st) + return AVERROR(ENOMEM); + sc = av_mallocz(sizeof(*sc)); + if (!sc) + return AVERROR(ENOMEM); + st->priv_data = sc; + + ret = av_get_packet(pb, &pkt, len); + if (ret < 0) + return ret; + + st->disposition |= AV_DISPOSITION_ATTACHED_PIC; + + st->attached_pic = pkt; + st->attached_pic.stream_index = st->index; + st->attached_pic.flags |= AV_PKT_FLAG_KEY; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = id; + + return 0; +} + +static int mov_metadata_raw(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) +{ + char *value = av_malloc(len + 1); + if (!value) + return AVERROR(ENOMEM); + avio_read(pb, value, len); + value[len] = 0; + return av_dict_set(&c->fc->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); +} + +static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ +#ifdef MOV_EXPORT_ALL_METADATA + char tmp_key[5]; +#endif + char str[1024], key2[16], language[4] = {0}; + const char *key = NULL; + uint16_t langcode = 0; + uint32_t data_type = 0, str_size; + int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL; + + if (c->itunes_metadata && atom.type == MKTAG('-','-','-','-')) + return mov_read_custom_metadata(c, pb, atom); + + switch (atom.type) { + case MKTAG(0xa9,'n','a','m'): key = "title"; break; + case MKTAG(0xa9,'a','u','t'): + case MKTAG(0xa9,'A','R','T'): key = "artist"; break; + case MKTAG( 'a','A','R','T'): key = "album_artist"; break; + case MKTAG(0xa9,'w','r','t'): key = "composer"; break; + case MKTAG( 'c','p','r','t'): + case MKTAG(0xa9,'c','p','y'): key = "copyright"; break; + case MKTAG(0xa9,'g','r','p'): key = "grouping"; break; + case MKTAG(0xa9,'l','y','r'): key = "lyrics"; break; + case MKTAG(0xa9,'c','m','t'): + case MKTAG(0xa9,'i','n','f'): key = "comment"; break; + case MKTAG(0xa9,'a','l','b'): key = "album"; break; + case MKTAG(0xa9,'d','a','y'): key = "date"; break; + case MKTAG(0xa9,'g','e','n'): key = "genre"; break; + case MKTAG( 'g','n','r','e'): key = "genre"; + parse = mov_metadata_gnre; break; + case MKTAG(0xa9,'t','o','o'): + case MKTAG(0xa9,'s','w','r'): key = "encoder"; break; + case MKTAG(0xa9,'e','n','c'): key = "encoder"; break; + case MKTAG(0xa9,'m','a','k'): key = "make"; break; + case MKTAG(0xa9,'m','o','d'): key = "model"; break; + case MKTAG(0xa9,'x','y','z'): key = "location"; break; + case MKTAG( 'd','e','s','c'): key = "description";break; + case MKTAG( 'l','d','e','s'): key = "synopsis"; break; + case MKTAG( 't','v','s','h'): key = "show"; break; + case MKTAG( 't','v','e','n'): key = "episode_id";break; + case MKTAG( 't','v','n','n'): key = "network"; break; + case MKTAG( 't','r','k','n'): key = "track"; + parse = mov_metadata_track_or_disc_number; break; + case MKTAG( 'd','i','s','k'): key = "disc"; + parse = mov_metadata_track_or_disc_number; break; + case MKTAG( 't','v','e','s'): key = "episode_sort"; + parse = mov_metadata_int8_bypass_padding; break; + case MKTAG( 't','v','s','n'): key = "season_number"; + parse = mov_metadata_int8_bypass_padding; break; + case MKTAG( 's','t','i','k'): key = "media_type"; + parse = mov_metadata_int8_no_padding; break; + case MKTAG( 'h','d','v','d'): key = "hd_video"; + parse = mov_metadata_int8_no_padding; break; + case MKTAG( 'p','g','a','p'): key = "gapless_playback"; + parse = mov_metadata_int8_no_padding; break; + case MKTAG( '@','P','R','M'): + return mov_metadata_raw(c, pb, atom.size, "premiere_version"); + case MKTAG( '@','P','R','Q'): + return mov_metadata_raw(c, pb, atom.size, "quicktime_version"); + } + + if (c->itunes_metadata && atom.size > 8) { + int data_size = avio_rb32(pb); + int tag = avio_rl32(pb); + if (tag == MKTAG('d','a','t','a')) { + data_type = avio_rb32(pb); // type + avio_rb32(pb); // unknown + str_size = data_size - 16; + atom.size -= 16; + + if (atom.type == MKTAG('c', 'o', 'v', 'r')) { + int ret = mov_read_covr(c, pb, data_type, str_size); + if (ret < 0) { + av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n"); + return ret; + } + } + } else return 0; + } else if (atom.size > 4 && key && !c->itunes_metadata) { + str_size = avio_rb16(pb); // string length + langcode = avio_rb16(pb); + ff_mov_lang_to_iso639(langcode, language); + atom.size -= 4; + } else + str_size = atom.size; + +#ifdef MOV_EXPORT_ALL_METADATA + if (!key) { + snprintf(tmp_key, 5, "%.4s", (char*)&atom.type); + key = tmp_key; + } +#endif + + if (!key) + return 0; + if (atom.size < 0) + return AVERROR_INVALIDDATA; + + str_size = FFMIN3(sizeof(str)-1, str_size, atom.size); + + if (parse) + parse(c, pb, str_size, key); + else { + if (data_type == 3 || (data_type == 0 && (langcode < 0x400 || langcode == 0x7fff))) { // MAC Encoded + mov_read_mac_string(c, pb, str_size, str, sizeof(str)); + } else { + avio_read(pb, str, str_size); + str[str_size] = 0; + } + av_dict_set(&c->fc->metadata, key, str, 0); + if (*language && strcmp(language, "und")) { + snprintf(key2, sizeof(key2), "%s-%s", key, language); + av_dict_set(&c->fc->metadata, key2, str, 0); + } + } + av_dlog(c->fc, "lang \"%3s\" ", language); + av_dlog(c->fc, "tag \"%s\" value \"%s\" atom \"%.4s\" %d %"PRId64"\n", + key, str, (char*)&atom.type, str_size, atom.size); + + return 0; +} + +static int mov_read_chpl(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t start; + int i, nb_chapters, str_len, version; + char str[256+1]; + + if ((atom.size -= 5) < 0) + return 0; + + version = avio_r8(pb); + avio_rb24(pb); + if (version) + avio_rb32(pb); // ??? + nb_chapters = avio_r8(pb); + + for (i = 0; i < nb_chapters; i++) { + if (atom.size < 9) + return 0; + + start = avio_rb64(pb); + str_len = avio_r8(pb); + + if ((atom.size -= 9+str_len) < 0) + return 0; + + avio_read(pb, str, str_len); + str[str_len] = 0; + avpriv_new_chapter(c->fc, i, (AVRational){1,10000000}, start, AV_NOPTS_VALUE, str); + } + return 0; +} + +#define MIN_DATA_ENTRY_BOX_SIZE 12 +static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + int entries, i, j; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_rb32(pb); // version + flags + entries = avio_rb32(pb); + if (entries > (atom.size - 1) / MIN_DATA_ENTRY_BOX_SIZE + 1 || + entries >= UINT_MAX / sizeof(*sc->drefs)) + return AVERROR_INVALIDDATA; + av_free(sc->drefs); + sc->drefs_count = 0; + sc->drefs = av_mallocz(entries * sizeof(*sc->drefs)); + if (!sc->drefs) + return AVERROR(ENOMEM); + sc->drefs_count = entries; + + for (i = 0; i < sc->drefs_count; i++) { + MOVDref *dref = &sc->drefs[i]; + uint32_t size = avio_rb32(pb); + int64_t next = avio_tell(pb) + size - 4; + + if (size < 12) + return AVERROR_INVALIDDATA; + + dref->type = avio_rl32(pb); + avio_rb32(pb); // version + flags + av_dlog(c->fc, "type %.4s size %d\n", (char*)&dref->type, size); + + if (dref->type == MKTAG('a','l','i','s') && size > 150) { + /* macintosh alias record */ + uint16_t volume_len, len; + int16_t type; + + avio_skip(pb, 10); + + volume_len = avio_r8(pb); + volume_len = FFMIN(volume_len, 27); + avio_read(pb, dref->volume, 27); + dref->volume[volume_len] = 0; + av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %d\n", dref->volume, volume_len); + + avio_skip(pb, 12); + + len = avio_r8(pb); + len = FFMIN(len, 63); + avio_read(pb, dref->filename, 63); + dref->filename[len] = 0; + av_log(c->fc, AV_LOG_DEBUG, "filename %s, len %d\n", dref->filename, len); + + avio_skip(pb, 16); + + /* read next level up_from_alias/down_to_target */ + dref->nlvl_from = avio_rb16(pb); + dref->nlvl_to = avio_rb16(pb); + av_log(c->fc, AV_LOG_DEBUG, "nlvl from %d, nlvl to %d\n", + dref->nlvl_from, dref->nlvl_to); + + avio_skip(pb, 16); + + for (type = 0; type != -1 && avio_tell(pb) < next; ) { + if(url_feof(pb)) + return AVERROR_EOF; + type = avio_rb16(pb); + len = avio_rb16(pb); + av_log(c->fc, AV_LOG_DEBUG, "type %d, len %d\n", type, len); + if (len&1) + len += 1; + if (type == 2) { // absolute path + av_free(dref->path); + dref->path = av_mallocz(len+1); + if (!dref->path) + return AVERROR(ENOMEM); + avio_read(pb, dref->path, len); + if (len > volume_len && !strncmp(dref->path, dref->volume, volume_len)) { + len -= volume_len; + memmove(dref->path, dref->path+volume_len, len); + dref->path[len] = 0; + } + for (j = 0; j < len; j++) + if (dref->path[j] == ':') + dref->path[j] = '/'; + av_log(c->fc, AV_LOG_DEBUG, "path %s\n", dref->path); + } else if (type == 0) { // directory name + av_free(dref->dir); + dref->dir = av_malloc(len+1); + if (!dref->dir) + return AVERROR(ENOMEM); + avio_read(pb, dref->dir, len); + dref->dir[len] = 0; + for (j = 0; j < len; j++) + if (dref->dir[j] == ':') + dref->dir[j] = '/'; + av_log(c->fc, AV_LOG_DEBUG, "dir %s\n", dref->dir); + } else + avio_skip(pb, len); + } + } + avio_seek(pb, next, SEEK_SET); + } + return 0; +} + +static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + uint32_t type; + uint32_t av_unused ctype; + int title_size; + char *title_str; + + if (c->fc->nb_streams < 1) // meta before first trak + return 0; + + st = c->fc->streams[c->fc->nb_streams-1]; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + /* component type */ + ctype = avio_rl32(pb); + type = avio_rl32(pb); /* component subtype */ + + av_dlog(c->fc, "ctype= %.4s (0x%08x)\n", (char*)&ctype, ctype); + av_dlog(c->fc, "stype= %.4s\n", (char*)&type); + + if (type == MKTAG('v','i','d','e')) + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + else if (type == MKTAG('s','o','u','n')) + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + else if (type == MKTAG('m','1','a',' ')) + st->codec->codec_id = AV_CODEC_ID_MP2; + else if ((type == MKTAG('s','u','b','p')) || (type == MKTAG('c','l','c','p'))) + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + + avio_rb32(pb); /* component manufacture */ + avio_rb32(pb); /* component flags */ + avio_rb32(pb); /* component flags mask */ + + title_size = atom.size - 24; + if (title_size > 0) { + title_str = av_malloc(title_size + 1); /* Add null terminator */ + if (!title_str) + return AVERROR(ENOMEM); + avio_read(pb, title_str, title_size); + title_str[title_size] = 0; + if (title_str[0]) + av_dict_set(&st->metadata, "handler_name", title_str + + (!c->isom && title_str[0] == title_size - 1), 0); + av_freep(&title_str); + } + + return 0; +} + +int ff_mov_read_esds(AVFormatContext *fc, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int tag; + + if (fc->nb_streams < 1) + return 0; + st = fc->streams[fc->nb_streams-1]; + + avio_rb32(pb); /* version + flags */ + ff_mp4_read_descr(fc, pb, &tag); + if (tag == MP4ESDescrTag) { + ff_mp4_parse_es_descr(pb, NULL); + } else + avio_rb16(pb); /* ID */ + + ff_mp4_read_descr(fc, pb, &tag); + if (tag == MP4DecConfigDescrTag) + ff_mp4_read_dec_config_descr(fc, st, pb); + return 0; +} + +static int mov_read_esds(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return ff_mov_read_esds(c->fc, pb, atom); +} + +static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int ac3info, acmod, lfeon, bsmod; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + ac3info = avio_rb24(pb); + bsmod = (ac3info >> 14) & 0x7; + acmod = (ac3info >> 11) & 0x7; + lfeon = (ac3info >> 10) & 0x1; + st->codec->channels = ((int[]){2,1,2,3,3,4,4,5})[acmod] + lfeon; + st->codec->channel_layout = avpriv_ac3_channel_layout_tab[acmod]; + if (lfeon) + st->codec->channel_layout |= AV_CH_LOW_FREQUENCY; + st->codec->audio_service_type = bsmod; + if (st->codec->channels > 1 && bsmod == 0x7) + st->codec->audio_service_type = AV_AUDIO_SERVICE_TYPE_KARAOKE; + + return 0; +} + +static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int eac3info, acmod, lfeon, bsmod; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + /* No need to parse fields for additional independent substreams and its + * associated dependent substreams since libavcodec's E-AC-3 decoder + * does not support them yet. */ + avio_rb16(pb); /* data_rate and num_ind_sub */ + eac3info = avio_rb24(pb); + bsmod = (eac3info >> 12) & 0x1f; + acmod = (eac3info >> 9) & 0x7; + lfeon = (eac3info >> 8) & 0x1; + st->codec->channel_layout = avpriv_ac3_channel_layout_tab[acmod]; + if (lfeon) + st->codec->channel_layout |= AV_CH_LOW_FREQUENCY; + st->codec->channels = av_get_channel_layout_nb_channels(st->codec->channel_layout); + st->codec->audio_service_type = bsmod; + if (st->codec->channels > 1 && bsmod == 0x7) + st->codec->audio_service_type = AV_AUDIO_SERVICE_TYPE_KARAOKE; + + return 0; +} + +static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (atom.size < 16) + return 0; + + /* skip version and flags */ + avio_skip(pb, 4); + + ff_mov_read_chan(c->fc, pb, st, atom.size - 4); + + return 0; +} + +static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (ff_get_wav_header(pb, st->codec, atom.size) < 0) { + av_log(c->fc, AV_LOG_WARNING, "get_wav_header failed\n"); + } + + return 0; +} + +static int mov_read_pasp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + const int num = avio_rb32(pb); + const int den = avio_rb32(pb); + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((st->sample_aspect_ratio.den != 1 || st->sample_aspect_ratio.num) && // default + (den != st->sample_aspect_ratio.den || num != st->sample_aspect_ratio.num)) { + av_log(c->fc, AV_LOG_WARNING, + "sample aspect ratio already set to %d:%d, ignoring 'pasp' atom (%d:%d)\n", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, + num, den); + } else if (den != 0) { + st->sample_aspect_ratio.num = num; + st->sample_aspect_ratio.den = den; + } + return 0; +} + +/* this atom contains actual media data */ +static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + if (atom.size == 0) /* wrong one (MP4) */ + return 0; + c->found_mdat=1; + return 0; /* now go for moov */ +} + +/* read major brand, minor version and compatible brands and store them as metadata */ +static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + uint32_t minor_ver; + int comp_brand_size; + char minor_ver_str[11]; /* 32 bit integer -> 10 digits + null */ + char* comp_brands_str; + uint8_t type[5] = {0}; + + avio_read(pb, type, 4); + if (strcmp(type, "qt ")) + c->isom = 1; + av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type); + av_dict_set(&c->fc->metadata, "major_brand", type, 0); + minor_ver = avio_rb32(pb); /* minor version */ + snprintf(minor_ver_str, sizeof(minor_ver_str), "%d", minor_ver); + av_dict_set(&c->fc->metadata, "minor_version", minor_ver_str, 0); + + comp_brand_size = atom.size - 8; + if (comp_brand_size < 0) + return AVERROR_INVALIDDATA; + comp_brands_str = av_malloc(comp_brand_size + 1); /* Add null terminator */ + if (!comp_brands_str) + return AVERROR(ENOMEM); + avio_read(pb, comp_brands_str, comp_brand_size); + comp_brands_str[comp_brand_size] = 0; + av_dict_set(&c->fc->metadata, "compatible_brands", comp_brands_str, 0); + av_freep(&comp_brands_str); + + return 0; +} + +/* this atom should contain all header atoms */ +static int mov_read_moov(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int ret; + + if ((ret = mov_read_default(c, pb, atom)) < 0) + return ret; + /* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */ + /* so we don't parse the whole file if over a network */ + c->found_moov=1; + return 0; /* now go for mdat */ +} + +static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + c->fragment.moof_offset = avio_tell(pb) - 8; + av_dlog(c->fc, "moof offset %"PRIx64"\n", c->fragment.moof_offset); + return mov_read_default(c, pb, atom); +} + +static void mov_metadata_creation_time(AVDictionary **metadata, int64_t time) +{ + char buffer[32]; + if (time) { + struct tm *ptm; + time_t timet; + if(time >= 2082844800) + time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ + timet = time; + ptm = gmtime(&timet); + if (!ptm) return; + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm); + av_dict_set(metadata, "creation_time", buffer, 0); + } +} + +static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + int version; + char language[4] = {0}; + unsigned lang; + int64_t creation_time; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + version = avio_r8(pb); + if (version > 1) { + avpriv_request_sample(c->fc, "Version %d", version); + return AVERROR_PATCHWELCOME; + } + avio_rb24(pb); /* flags */ + if (version == 1) { + creation_time = avio_rb64(pb); + avio_rb64(pb); + } else { + creation_time = avio_rb32(pb); + avio_rb32(pb); /* modification time */ + } + mov_metadata_creation_time(&st->metadata, creation_time); + + sc->time_scale = avio_rb32(pb); + st->duration = (version == 1) ? avio_rb64(pb) : avio_rb32(pb); /* duration */ + + lang = avio_rb16(pb); /* language */ + if (ff_mov_lang_to_iso639(lang, language)) + av_dict_set(&st->metadata, "language", language, 0); + avio_rb16(pb); /* quality */ + + return 0; +} + +static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t creation_time; + int version = avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + if (version == 1) { + creation_time = avio_rb64(pb); + avio_rb64(pb); + } else { + creation_time = avio_rb32(pb); + avio_rb32(pb); /* modification time */ + } + mov_metadata_creation_time(&c->fc->metadata, creation_time); + c->time_scale = avio_rb32(pb); /* time scale */ + + av_dlog(c->fc, "time scale = %i\n", c->time_scale); + + c->duration = (version == 1) ? avio_rb64(pb) : avio_rb32(pb); /* duration */ + // set the AVCodecContext duration because the duration of individual tracks + // may be inaccurate + if (c->time_scale > 0) + c->fc->duration = av_rescale(c->duration, AV_TIME_BASE, c->time_scale); + avio_rb32(pb); /* preferred scale */ + + avio_rb16(pb); /* preferred volume */ + + avio_skip(pb, 10); /* reserved */ + + avio_skip(pb, 36); /* display matrix */ + + avio_rb32(pb); /* preview time */ + avio_rb32(pb); /* preview duration */ + avio_rb32(pb); /* poster time */ + avio_rb32(pb); /* selection time */ + avio_rb32(pb); /* selection duration */ + avio_rb32(pb); /* current time */ + avio_rb32(pb); /* next track ID */ + return 0; +} + +static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int little_endian; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + little_endian = avio_rb16(pb) & 0xFF; + av_dlog(c->fc, "enda %d\n", little_endian); + if (little_endian == 1) { + switch (st->codec->codec_id) { + case AV_CODEC_ID_PCM_S24BE: + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; + break; + case AV_CODEC_ID_PCM_S32BE: + st->codec->codec_id = AV_CODEC_ID_PCM_S32LE; + break; + case AV_CODEC_ID_PCM_F32BE: + st->codec->codec_id = AV_CODEC_ID_PCM_F32LE; + break; + case AV_CODEC_ID_PCM_F64BE: + st->codec->codec_id = AV_CODEC_ID_PCM_F64LE; + break; + default: + break; + } + } + return 0; +} + +static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + unsigned mov_field_order; + enum AVFieldOrder decoded_field_order = AV_FIELD_UNKNOWN; + + if (c->fc->nb_streams < 1) // will happen with jp2 files + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + if (atom.size < 2) + return AVERROR_INVALIDDATA; + mov_field_order = avio_rb16(pb); + if ((mov_field_order & 0xFF00) == 0x0100) + decoded_field_order = AV_FIELD_PROGRESSIVE; + else if ((mov_field_order & 0xFF00) == 0x0200) { + switch (mov_field_order & 0xFF) { + case 0x01: decoded_field_order = AV_FIELD_TT; + break; + case 0x06: decoded_field_order = AV_FIELD_BB; + break; + case 0x09: decoded_field_order = AV_FIELD_TB; + break; + case 0x0E: decoded_field_order = AV_FIELD_BT; + break; + } + } + if (decoded_field_order == AV_FIELD_UNKNOWN && mov_field_order) { + av_log(NULL, AV_LOG_ERROR, "Unknown MOV field order 0x%04x\n", mov_field_order); + } + st->codec->field_order = decoded_field_order; + + return 0; +} + +/* FIXME modify qdm2/svq3/h264 decoders to take full atom as extradata */ +static int mov_read_extradata(MOVContext *c, AVIOContext *pb, MOVAtom atom, + enum AVCodecID codec_id) +{ + AVStream *st; + uint64_t size; + uint8_t *buf; + + if (c->fc->nb_streams < 1) // will happen with jp2 files + return 0; + st= c->fc->streams[c->fc->nb_streams-1]; + + if (st->codec->codec_id != codec_id) + return 0; /* unexpected codec_id - don't mess with extradata */ + + size= (uint64_t)st->codec->extradata_size + atom.size + 8 + FF_INPUT_BUFFER_PADDING_SIZE; + if (size > INT_MAX || (uint64_t)atom.size > INT_MAX) + return AVERROR_INVALIDDATA; + buf= av_realloc(st->codec->extradata, size); + if (!buf) + return AVERROR(ENOMEM); + st->codec->extradata= buf; + buf+= st->codec->extradata_size; + st->codec->extradata_size= size - FF_INPUT_BUFFER_PADDING_SIZE; + AV_WB32( buf , atom.size + 8); + AV_WL32( buf + 4, atom.type); + avio_read(pb, buf + 8, atom.size); + return 0; +} + +/* wrapper functions for reading ALAC/AVS/MJPEG/MJPEG2000 extradata atoms only for those codecs */ +static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_ALAC); +} + +static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS); +} + +static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_JPEG2000); +} + +static int mov_read_avid(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVUI); +} + +static int mov_read_svq3(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_SVQ3); +} + +static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; + + if (st->codec->codec_id == AV_CODEC_ID_QDM2 || + st->codec->codec_id == AV_CODEC_ID_QDMC || + st->codec->codec_id == AV_CODEC_ID_SPEEX) { + // pass all frma atom to codec, needed at least for QDMC and QDM2 + av_free(st->codec->extradata); + st->codec->extradata_size = 0; + st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = atom.size; + avio_read(pb, st->codec->extradata, atom.size); + } else if (atom.size > 8) { /* to read frma, esds atoms */ + int ret; + if ((ret = mov_read_default(c, pb, atom)) < 0) + return ret; + } else + avio_skip(pb, atom.size); + return 0; +} + +/** + * This function reads atom content and puts data in extradata without tag + * nor size unlike mov_read_extradata. + */ +static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; + + if (atom.size >= 10) { + // Broken files created by legacy versions of libavformat will + // wrap a whole fiel atom inside of a glbl atom. + unsigned size = avio_rb32(pb); + unsigned type = avio_rl32(pb); + avio_seek(pb, -8, SEEK_CUR); + if (type == MKTAG('f','i','e','l') && size == atom.size) + return mov_read_default(c, pb, atom); + } + av_free(st->codec->extradata); + st->codec->extradata_size = 0; + st->codec->extradata = av_mallocz(atom.size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = atom.size; + avio_read(pb, st->codec->extradata, atom.size); + return 0; +} + +static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + uint8_t profile_level; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (atom.size >= (1<<28) || atom.size < 7) + return AVERROR_INVALIDDATA; + + profile_level = avio_r8(pb); + if ((profile_level & 0xf0) != 0xc0) + return 0; + + av_free(st->codec->extradata); + st->codec->extradata_size = 0; + st->codec->extradata = av_mallocz(atom.size - 7 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = atom.size - 7; + avio_seek(pb, 6, SEEK_CUR); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + return 0; +} + +/** + * An strf atom is a BITMAPINFOHEADER struct. This struct is 40 bytes itself, + * but can have extradata appended at the end after the 40 bytes belonging + * to the struct. + */ +static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + if (atom.size <= 40) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((uint64_t)atom.size > (1<<30)) + return AVERROR_INVALIDDATA; + + av_free(st->codec->extradata); + st->codec->extradata_size = 0; + st->codec->extradata = av_mallocz(atom.size - 40 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!st->codec->extradata) + return AVERROR(ENOMEM); + st->codec->extradata_size = atom.size - 40; + avio_skip(pb, 40); + avio_read(pb, st->codec->extradata, atom.size - 40); + return 0; +} + +static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + entries = avio_rb32(pb); + + if (!entries) + return 0; + if (entries >= UINT_MAX/sizeof(int64_t)) + return AVERROR_INVALIDDATA; + + sc->chunk_offsets = av_malloc(entries * sizeof(int64_t)); + if (!sc->chunk_offsets) + return AVERROR(ENOMEM); + sc->chunk_count = entries; + + if (atom.type == MKTAG('s','t','c','o')) + for (i = 0; i < entries && !pb->eof_reached; i++) + sc->chunk_offsets[i] = avio_rb32(pb); + else if (atom.type == MKTAG('c','o','6','4')) + for (i = 0; i < entries && !pb->eof_reached; i++) + sc->chunk_offsets[i] = avio_rb64(pb); + else + return AVERROR_INVALIDDATA; + + sc->chunk_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + return 0; +} + +/** + * Compute codec id for 'lpcm' tag. + * See CoreAudioTypes and AudioStreamBasicDescription at Apple. + */ +enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags) +{ + /* lpcm flags: + * 0x1 = float + * 0x2 = big-endian + * 0x4 = signed + */ + return ff_get_pcm_codec_id(bps, flags & 1, flags & 2, flags & 4 ? -1 : 0); +} + +int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries) +{ + AVStream *st; + MOVStreamContext *sc; + int j, pseudo_stream_id; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + for (pseudo_stream_id = 0; + pseudo_stream_id < entries && !pb->eof_reached; + pseudo_stream_id++) { + //Parsing Sample description table + enum AVCodecID id; + int dref_id = 1; + MOVAtom a = { AV_RL32("stsd") }; + int64_t start_pos = avio_tell(pb); + int64_t size = avio_rb32(pb); /* size */ + uint32_t format = avio_rl32(pb); /* data format */ + + if (size >= 16) { + avio_rb32(pb); /* reserved */ + avio_rb16(pb); /* reserved */ + dref_id = avio_rb16(pb); + }else if (size <= 7){ + av_log(c->fc, AV_LOG_ERROR, "invalid size %"PRId64" in stsd\n", size); + return AVERROR_INVALIDDATA; + } + + if (st->codec->codec_tag && + st->codec->codec_tag != format && + (c->fc->video_codec_id ? ff_codec_get_id(ff_codec_movvideo_tags, format) != c->fc->video_codec_id + : st->codec->codec_tag != MKTAG('j','p','e','g')) + ){ + /* Multiple fourcc, we skip JPEG. This is not correct, we should + * export it as a separate AVStream but this needs a few changes + * in the MOV demuxer, patch welcome. */ + av_log(c->fc, AV_LOG_WARNING, "multiple fourcc not supported\n"); + avio_skip(pb, size - (avio_tell(pb) - start_pos)); + continue; + } + /* we cannot demux concatenated h264 streams because of different extradata */ + if (st->codec->codec_tag && st->codec->codec_tag == AV_RL32("avc1")) + av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 might not play corrently.\n"); + sc->pseudo_stream_id = st->codec->codec_tag ? -1 : pseudo_stream_id; + sc->dref_id= dref_id; + + st->codec->codec_tag = format; + id = ff_codec_get_id(ff_codec_movaudio_tags, format); + if (id<=0 && ((format&0xFFFF) == 'm'+('s'<<8) || (format&0xFFFF) == 'T'+('S'<<8))) + id = ff_codec_get_id(ff_codec_wav_tags, av_bswap32(format)&0xFFFF); + + if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO && id > 0) { + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + } else if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO && /* do not overwrite codec type */ + format && format != MKTAG('m','p','4','s')) { /* skip old asf mpeg4 tag */ + id = ff_codec_get_id(ff_codec_movvideo_tags, format); + if (id <= 0) + id = ff_codec_get_id(ff_codec_bmp_tags, format); + if (id > 0) + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + else if (st->codec->codec_type == AVMEDIA_TYPE_DATA || + (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE && + st->codec->codec_id == AV_CODEC_ID_NONE)){ + id = ff_codec_get_id(ff_codec_movsubtitle_tags, format); + if (id > 0) + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + } + } + + av_dlog(c->fc, "size=%"PRId64" 4CC= %c%c%c%c codec_type=%d\n", size, + (format >> 0) & 0xff, (format >> 8) & 0xff, (format >> 16) & 0xff, + (format >> 24) & 0xff, st->codec->codec_type); + + if (st->codec->codec_type==AVMEDIA_TYPE_VIDEO) { + unsigned int color_depth, len; + int color_greyscale; + int color_table_id; + + st->codec->codec_id = id; + avio_rb16(pb); /* version */ + avio_rb16(pb); /* revision level */ + avio_rb32(pb); /* vendor */ + avio_rb32(pb); /* temporal quality */ + avio_rb32(pb); /* spatial quality */ + + st->codec->width = avio_rb16(pb); /* width */ + st->codec->height = avio_rb16(pb); /* height */ + + avio_rb32(pb); /* horiz resolution */ + avio_rb32(pb); /* vert resolution */ + avio_rb32(pb); /* data size, always 0 */ + avio_rb16(pb); /* frames per samples */ + + len = avio_r8(pb); /* codec name, pascal string */ + if (len > 31) + len = 31; + mov_read_mac_string(c, pb, len, st->codec->codec_name, 32); + if (len < 31) + avio_skip(pb, 31 - len); + /* codec_tag YV12 triggers an UV swap in rawdec.c */ + if (!memcmp(st->codec->codec_name, "Planar Y'CbCr 8-bit 4:2:0", 25)){ + st->codec->codec_tag=MKTAG('I', '4', '2', '0'); + st->codec->width &= ~1; + st->codec->height &= ~1; + } + /* Flash Media Server uses tag H263 with Sorenson Spark */ + if (format == MKTAG('H','2','6','3') && + !memcmp(st->codec->codec_name, "Sorenson H263", 13)) + st->codec->codec_id = AV_CODEC_ID_FLV1; + + st->codec->bits_per_coded_sample = avio_rb16(pb); /* depth */ + color_table_id = avio_rb16(pb); /* colortable id */ + av_dlog(c->fc, "depth %d, ctab id %d\n", + st->codec->bits_per_coded_sample, color_table_id); + /* figure out the palette situation */ + color_depth = st->codec->bits_per_coded_sample & 0x1F; + color_greyscale = st->codec->bits_per_coded_sample & 0x20; + + /* if the depth is 2, 4, or 8 bpp, file is palettized */ + if ((color_depth == 2) || (color_depth == 4) || + (color_depth == 8)) { + /* for palette traversal */ + unsigned int color_start, color_count, color_end; + unsigned char a, r, g, b; + + if (color_greyscale) { + int color_index, color_dec; + /* compute the greyscale palette */ + st->codec->bits_per_coded_sample = color_depth; + color_count = 1 << color_depth; + color_index = 255; + color_dec = 256 / (color_count - 1); + for (j = 0; j < color_count; j++) { + if (id == AV_CODEC_ID_CINEPAK){ + r = g = b = color_count - 1 - color_index; + }else + r = g = b = color_index; + sc->palette[j] = + (0xFFU << 24) | (r << 16) | (g << 8) | (b); + color_index -= color_dec; + if (color_index < 0) + color_index = 0; + } + } else if (color_table_id) { + const uint8_t *color_table; + /* if flag bit 3 is set, use the default palette */ + color_count = 1 << color_depth; + if (color_depth == 2) + color_table = ff_qt_default_palette_4; + else if (color_depth == 4) + color_table = ff_qt_default_palette_16; + else + color_table = ff_qt_default_palette_256; + + for (j = 0; j < color_count; j++) { + r = color_table[j * 3 + 0]; + g = color_table[j * 3 + 1]; + b = color_table[j * 3 + 2]; + sc->palette[j] = + (0xFFU << 24) | (r << 16) | (g << 8) | (b); + } + } else { + /* load the palette from the file */ + color_start = avio_rb32(pb); + color_count = avio_rb16(pb); + color_end = avio_rb16(pb); + if ((color_start <= 255) && + (color_end <= 255)) { + for (j = color_start; j <= color_end; j++) { + /* each A, R, G, or B component is 16 bits; + * only use the top 8 bits */ + a = avio_r8(pb); + avio_r8(pb); + r = avio_r8(pb); + avio_r8(pb); + g = avio_r8(pb); + avio_r8(pb); + b = avio_r8(pb); + avio_r8(pb); + sc->palette[j] = + (a << 24 ) | (r << 16) | (g << 8) | (b); + } + } + } + sc->has_palette = 1; + } + } else if (st->codec->codec_type==AVMEDIA_TYPE_AUDIO) { + int bits_per_sample, flags; + uint16_t version = avio_rb16(pb); + AVDictionaryEntry *compatible_brands = av_dict_get(c->fc->metadata, "compatible_brands", NULL, AV_DICT_MATCH_CASE); + + st->codec->codec_id = id; + avio_rb16(pb); /* revision level */ + avio_rb32(pb); /* vendor */ + + st->codec->channels = avio_rb16(pb); /* channel count */ + av_dlog(c->fc, "audio channels %d\n", st->codec->channels); + st->codec->bits_per_coded_sample = avio_rb16(pb); /* sample size */ + + sc->audio_cid = avio_rb16(pb); + avio_rb16(pb); /* packet size = 0 */ + + st->codec->sample_rate = ((avio_rb32(pb) >> 16)); + + //Read QT version 1 fields. In version 0 these do not exist. + av_dlog(c->fc, "version =%d, isom =%d\n",version,c->isom); + if (!c->isom || + (compatible_brands && strstr(compatible_brands->value, "qt "))) { + if (version==1) { + sc->samples_per_frame = avio_rb32(pb); + avio_rb32(pb); /* bytes per packet */ + sc->bytes_per_frame = avio_rb32(pb); + avio_rb32(pb); /* bytes per sample */ + } else if (version==2) { + avio_rb32(pb); /* sizeof struct only */ + st->codec->sample_rate = av_int2double(avio_rb64(pb)); /* float 64 */ + st->codec->channels = avio_rb32(pb); + avio_rb32(pb); /* always 0x7F000000 */ + st->codec->bits_per_coded_sample = avio_rb32(pb); /* bits per channel if sound is uncompressed */ + flags = avio_rb32(pb); /* lpcm format specific flag */ + sc->bytes_per_frame = avio_rb32(pb); /* bytes per audio packet if constant */ + sc->samples_per_frame = avio_rb32(pb); /* lpcm frames per audio packet if constant */ + if (format == MKTAG('l','p','c','m')) + st->codec->codec_id = ff_mov_get_lpcm_codec_id(st->codec->bits_per_coded_sample, flags); + } + } + + switch (st->codec->codec_id) { + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_U8: + if (st->codec->bits_per_coded_sample == 16) + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + break; + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S16BE: + if (st->codec->bits_per_coded_sample == 8) + st->codec->codec_id = AV_CODEC_ID_PCM_S8; + else if (st->codec->bits_per_coded_sample == 24) + st->codec->codec_id = + st->codec->codec_id == AV_CODEC_ID_PCM_S16BE ? + AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE; + break; + /* set values for old format before stsd version 1 appeared */ + case AV_CODEC_ID_MACE3: + sc->samples_per_frame = 6; + sc->bytes_per_frame = 2*st->codec->channels; + break; + case AV_CODEC_ID_MACE6: + sc->samples_per_frame = 6; + sc->bytes_per_frame = 1*st->codec->channels; + break; + case AV_CODEC_ID_ADPCM_IMA_QT: + sc->samples_per_frame = 64; + sc->bytes_per_frame = 34*st->codec->channels; + break; + case AV_CODEC_ID_GSM: + sc->samples_per_frame = 160; + sc->bytes_per_frame = 33; + break; + default: + break; + } + + bits_per_sample = av_get_bits_per_sample(st->codec->codec_id); + if (bits_per_sample) { + st->codec->bits_per_coded_sample = bits_per_sample; + sc->sample_size = (bits_per_sample >> 3) * st->codec->channels; + } + } else if (st->codec->codec_type==AVMEDIA_TYPE_SUBTITLE){ + // ttxt stsd contains display flags, justification, background + // color, fonts, and default styles, so fake an atom to read it + MOVAtom fake_atom = { .size = size - (avio_tell(pb) - start_pos) }; + if (format != AV_RL32("mp4s")) // mp4s contains a regular esds atom + mov_read_glbl(c, pb, fake_atom); + st->codec->codec_id= id; + st->codec->width = sc->width; + st->codec->height = sc->height; + } else { + if (st->codec->codec_tag == MKTAG('t','m','c','d')) { + MOVStreamContext *tmcd_ctx = st->priv_data; + int val; + avio_rb32(pb); /* reserved */ + val = avio_rb32(pb); /* flags */ + tmcd_ctx->tmcd_flags = val; + if (val & 1) + st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE; + avio_rb32(pb); /* time scale */ + avio_rb32(pb); /* frame duration */ + st->codec->time_base.den = avio_r8(pb); /* number of frame */ + st->codec->time_base.num = 1; + } + /* other codec type, just skip (rtp, mp4s, ...) */ + avio_skip(pb, size - (avio_tell(pb) - start_pos)); + } + /* this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) */ + a.size = size - (avio_tell(pb) - start_pos); + if (a.size > 8) { + int ret; + if ((ret = mov_read_default(c, pb, a)) < 0) + return ret; + } else if (a.size > 0) + avio_skip(pb, a.size); + } + + if (pb->eof_reached) + return AVERROR_EOF; + + if (st->codec->codec_type==AVMEDIA_TYPE_AUDIO && st->codec->sample_rate==0 && sc->time_scale>1) + st->codec->sample_rate= sc->time_scale; + + /* special codec parameters handling */ + switch (st->codec->codec_id) { +#if CONFIG_DV_DEMUXER + case AV_CODEC_ID_DVAUDIO: + c->dv_fctx = avformat_alloc_context(); + c->dv_demux = avpriv_dv_init_demux(c->dv_fctx); + if (!c->dv_demux) { + av_log(c->fc, AV_LOG_ERROR, "dv demux context init error\n"); + return AVERROR(ENOMEM); + } + sc->dv_audio_container = 1; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + break; +#endif + /* no ifdef since parameters are always those */ + case AV_CODEC_ID_QCELP: + // force sample rate for qcelp when not stored in mov + if (st->codec->codec_tag != MKTAG('Q','c','l','p')) + st->codec->sample_rate = 8000; + st->codec->channels= 1; /* really needed */ + break; + case AV_CODEC_ID_AMR_NB: + st->codec->channels= 1; /* really needed */ + /* force sample rate for amr, stsd in 3gp does not store sample rate */ + st->codec->sample_rate = 8000; + break; + case AV_CODEC_ID_AMR_WB: + st->codec->channels = 1; + st->codec->sample_rate = 16000; + break; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; /* force type after stsd for m1a hdlr */ + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case AV_CODEC_ID_GSM: + case AV_CODEC_ID_ADPCM_MS: + case AV_CODEC_ID_ADPCM_IMA_WAV: + case AV_CODEC_ID_ILBC: + st->codec->block_align = sc->bytes_per_frame; + break; + case AV_CODEC_ID_ALAC: + if (st->codec->extradata_size == 36) { + st->codec->channels = AV_RB8 (st->codec->extradata+21); + st->codec->sample_rate = AV_RB32(st->codec->extradata+32); + } + break; + case AV_CODEC_ID_AC3: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case AV_CODEC_ID_MPEG1VIDEO: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + case AV_CODEC_ID_VC1: + st->need_parsing = AVSTREAM_PARSE_FULL; + break; + default: + break; + } + + return 0; +} + +static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int entries; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + entries = avio_rb32(pb); + + return ff_mov_read_stsd_entries(c, pb, entries); +} + +static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + entries = avio_rb32(pb); + + av_dlog(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); + + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->stsc_data)) + return AVERROR_INVALIDDATA; + sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data)); + if (!sc->stsc_data) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + sc->stsc_data[i].first = avio_rb32(pb); + sc->stsc_data[i].count = avio_rb32(pb); + sc->stsc_data[i].id = avio_rb32(pb); + } + + sc->stsc_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + return 0; +} + +static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_rb32(pb); // version + flags + + entries = avio_rb32(pb); + if (entries >= UINT_MAX / sizeof(*sc->stps_data)) + return AVERROR_INVALIDDATA; + sc->stps_data = av_malloc(entries * sizeof(*sc->stps_data)); + if (!sc->stps_data) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + sc->stps_data[i] = avio_rb32(pb); + //av_dlog(c->fc, "stps %d\n", sc->stps_data[i]); + } + + sc->stps_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + return 0; +} + +static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + entries = avio_rb32(pb); + + av_dlog(c->fc, "keyframe_count = %d\n", entries); + + if (!entries) + { + sc->keyframe_absent = 1; + return 0; + } + if (entries >= UINT_MAX / sizeof(int)) + return AVERROR_INVALIDDATA; + sc->keyframes = av_malloc(entries * sizeof(int)); + if (!sc->keyframes) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + sc->keyframes[i] = avio_rb32(pb); + //av_dlog(c->fc, "keyframes[]=%d\n", sc->keyframes[i]); + } + + sc->keyframe_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + return 0; +} + +static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries, sample_size, field_size, num_bytes; + GetBitContext gb; + unsigned char* buf; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + if (atom.type == MKTAG('s','t','s','z')) { + sample_size = avio_rb32(pb); + if (!sc->sample_size) /* do not overwrite value computed in stsd */ + sc->sample_size = sample_size; + sc->alt_sample_size = sample_size; + field_size = 32; + } else { + sample_size = 0; + avio_rb24(pb); /* reserved */ + field_size = avio_r8(pb); + } + entries = avio_rb32(pb); + + av_dlog(c->fc, "sample_size = %d sample_count = %d\n", sc->sample_size, entries); + + sc->sample_count = entries; + if (sample_size) + return 0; + + if (field_size != 4 && field_size != 8 && field_size != 16 && field_size != 32) { + av_log(c->fc, AV_LOG_ERROR, "Invalid sample field size %d\n", field_size); + return AVERROR_INVALIDDATA; + } + + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(int) || entries >= (UINT_MAX - 4) / field_size) + return AVERROR_INVALIDDATA; + sc->sample_sizes = av_malloc(entries * sizeof(int)); + if (!sc->sample_sizes) + return AVERROR(ENOMEM); + + num_bytes = (entries*field_size+4)>>3; + + buf = av_malloc(num_bytes+FF_INPUT_BUFFER_PADDING_SIZE); + if (!buf) { + av_freep(&sc->sample_sizes); + return AVERROR(ENOMEM); + } + + if (avio_read(pb, buf, num_bytes) < num_bytes) { + av_freep(&sc->sample_sizes); + av_free(buf); + return AVERROR_INVALIDDATA; + } + + init_get_bits(&gb, buf, 8*num_bytes); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + sc->sample_sizes[i] = get_bits_long(&gb, field_size); + sc->data_size += sc->sample_sizes[i]; + } + + sc->sample_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + av_free(buf); + return 0; +} + +static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + int64_t duration=0; + int64_t total_sample_count=0; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + entries = avio_rb32(pb); + + av_dlog(c->fc, "track[%i].stts.entries = %i\n", + c->fc->nb_streams-1, entries); + + if (entries >= UINT_MAX / sizeof(*sc->stts_data)) + return -1; + + sc->stts_data = av_malloc(entries * sizeof(*sc->stts_data)); + if (!sc->stts_data) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + int sample_duration; + int sample_count; + + sample_count=avio_rb32(pb); + sample_duration = avio_rb32(pb); + /* sample_duration < 0 is invalid based on the spec */ + if (sample_duration < 0) { + av_log(c->fc, AV_LOG_ERROR, "Invalid SampleDelta in STTS %d\n", sample_duration); + sample_duration = 1; + } + sc->stts_data[i].count= sample_count; + sc->stts_data[i].duration= sample_duration; + + av_dlog(c->fc, "sample_count=%d, sample_duration=%d\n", + sample_count, sample_duration); + + duration+=(int64_t)sample_duration*sample_count; + total_sample_count+=sample_count; + } + + sc->stts_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + st->nb_frames= total_sample_count; + if (duration) + st->duration= duration; + sc->track_end = duration; + return 0; +} + +static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + entries = avio_rb32(pb); + + av_dlog(c->fc, "track[%i].ctts.entries = %i\n", c->fc->nb_streams-1, entries); + + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->ctts_data)) + return AVERROR_INVALIDDATA; + sc->ctts_data = av_malloc(entries * sizeof(*sc->ctts_data)); + if (!sc->ctts_data) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + int count =avio_rb32(pb); + int duration =avio_rb32(pb); + + sc->ctts_data[i].count = count; + sc->ctts_data[i].duration= duration; + + av_dlog(c->fc, "count=%d, duration=%d\n", + count, duration); + + if (FFABS(duration) > (1<<28) && i+2fc, AV_LOG_WARNING, "CTTS invalid\n"); + av_freep(&sc->ctts_data); + sc->ctts_count = 0; + return 0; + } + + if (duration < 0 && i+2dts_shift = FFMAX(sc->dts_shift, -duration); + } + + sc->ctts_count = i; + + if (pb->eof_reached) + return AVERROR_EOF; + + av_dlog(c->fc, "dts shift %d\n", sc->dts_shift); + + return 0; +} + +static int mov_read_sbgp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int i, entries; + uint8_t version; + uint32_t grouping_type; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + version = avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + grouping_type = avio_rl32(pb); + if (grouping_type != MKTAG( 'r','a','p',' ')) + return 0; /* only support 'rap ' grouping */ + if (version == 1) + avio_rb32(pb); /* grouping_type_parameter */ + + entries = avio_rb32(pb); + if (!entries) + return 0; + if (entries >= UINT_MAX / sizeof(*sc->rap_group)) + return AVERROR_INVALIDDATA; + sc->rap_group = av_malloc(entries * sizeof(*sc->rap_group)); + if (!sc->rap_group) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) { + sc->rap_group[i].count = avio_rb32(pb); /* sample_count */ + sc->rap_group[i].index = avio_rb32(pb); /* group_description_index */ + } + + sc->rap_group_count = i; + + return pb->eof_reached ? AVERROR_EOF : 0; +} + +static void mov_build_index(MOVContext *mov, AVStream *st) +{ + MOVStreamContext *sc = st->priv_data; + int64_t current_offset; + int64_t current_dts = 0; + unsigned int stts_index = 0; + unsigned int stsc_index = 0; + unsigned int stss_index = 0; + unsigned int stps_index = 0; + unsigned int i, j; + uint64_t stream_size = 0; + AVIndexEntry *mem; + + /* adjust first dts according to edit list */ + if ((sc->empty_duration || sc->start_time) && mov->time_scale > 0) { + if (sc->empty_duration) + sc->empty_duration = av_rescale(sc->empty_duration, sc->time_scale, mov->time_scale); + sc->time_offset = sc->start_time - sc->empty_duration; + current_dts = -sc->time_offset; + if (sc->ctts_count>0 && sc->stts_count>0 && + sc->ctts_data[0].duration / FFMAX(sc->stts_data[0].duration, 1) > 16) { + /* more than 16 frames delay, dts are likely wrong + this happens with files created by iMovie */ + sc->wrong_dts = 1; + st->codec->has_b_frames = 1; + } + } + + /* only use old uncompressed audio chunk demuxing when stts specifies it */ + if (!(st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + sc->stts_count == 1 && sc->stts_data[0].duration == 1)) { + unsigned int current_sample = 0; + unsigned int stts_sample = 0; + unsigned int sample_size; + unsigned int distance = 0; + unsigned int rap_group_index = 0; + unsigned int rap_group_sample = 0; + int rap_group_present = sc->rap_group_count && sc->rap_group; + int key_off = (sc->keyframe_count && sc->keyframes[0] > 0) || (sc->stps_count && sc->stps_data[0] > 0); + + current_dts -= sc->dts_shift; + + if (!sc->sample_count || st->nb_index_entries) + return; + if (sc->sample_count >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries) + return; + mem = av_realloc(st->index_entries, (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries)); + if (!mem) + return; + st->index_entries = mem; + st->index_entries_allocated_size = (st->nb_index_entries + sc->sample_count) * sizeof(*st->index_entries); + + for (i = 0; i < sc->chunk_count; i++) { + current_offset = sc->chunk_offsets[i]; + while (stsc_index + 1 < sc->stsc_count && + i + 1 == sc->stsc_data[stsc_index + 1].first) + stsc_index++; + for (j = 0; j < sc->stsc_data[stsc_index].count; j++) { + int keyframe = 0; + if (current_sample >= sc->sample_count) { + av_log(mov->fc, AV_LOG_ERROR, "wrong sample count\n"); + return; + } + + if (!sc->keyframe_absent && (!sc->keyframe_count || current_sample+key_off == sc->keyframes[stss_index])) { + keyframe = 1; + if (stss_index + 1 < sc->keyframe_count) + stss_index++; + } else if (sc->stps_count && current_sample+key_off == sc->stps_data[stps_index]) { + keyframe = 1; + if (stps_index + 1 < sc->stps_count) + stps_index++; + } + if (rap_group_present && rap_group_index < sc->rap_group_count) { + if (sc->rap_group[rap_group_index].index > 0) + keyframe = 1; + if (++rap_group_sample == sc->rap_group[rap_group_index].count) { + rap_group_sample = 0; + rap_group_index++; + } + } + if (keyframe) + distance = 0; + sample_size = sc->alt_sample_size > 0 ? sc->alt_sample_size : sc->sample_sizes[current_sample]; + if (sc->pseudo_stream_id == -1 || + sc->stsc_data[stsc_index].id - 1 == sc->pseudo_stream_id) { + AVIndexEntry *e = &st->index_entries[st->nb_index_entries++]; + e->pos = current_offset; + e->timestamp = current_dts; + e->size = sample_size; + e->min_distance = distance; + e->flags = keyframe ? AVINDEX_KEYFRAME : 0; + av_dlog(mov->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", " + "size %d, distance %d, keyframe %d\n", st->index, current_sample, + current_offset, current_dts, sample_size, distance, keyframe); + } + + current_offset += sample_size; + stream_size += sample_size; + current_dts += sc->stts_data[stts_index].duration; + distance++; + stts_sample++; + current_sample++; + if (stts_index + 1 < sc->stts_count && stts_sample == sc->stts_data[stts_index].count) { + stts_sample = 0; + stts_index++; + } + } + } + if (st->duration > 0) + st->codec->bit_rate = stream_size*8*sc->time_scale/st->duration; + } else { + unsigned chunk_samples, total = 0; + + // compute total chunk count + for (i = 0; i < sc->stsc_count; i++) { + unsigned count, chunk_count; + + chunk_samples = sc->stsc_data[i].count; + if (i != sc->stsc_count - 1 && + sc->samples_per_frame && chunk_samples % sc->samples_per_frame) { + av_log(mov->fc, AV_LOG_ERROR, "error unaligned chunk\n"); + return; + } + + if (sc->samples_per_frame >= 160) { // gsm + count = chunk_samples / sc->samples_per_frame; + } else if (sc->samples_per_frame > 1) { + unsigned samples = (1024/sc->samples_per_frame)*sc->samples_per_frame; + count = (chunk_samples+samples-1) / samples; + } else { + count = (chunk_samples+1023) / 1024; + } + + if (i < sc->stsc_count - 1) + chunk_count = sc->stsc_data[i+1].first - sc->stsc_data[i].first; + else + chunk_count = sc->chunk_count - (sc->stsc_data[i].first - 1); + total += chunk_count * count; + } + + av_dlog(mov->fc, "chunk count %d\n", total); + if (total >= UINT_MAX / sizeof(*st->index_entries) - st->nb_index_entries) + return; + mem = av_realloc(st->index_entries, (st->nb_index_entries + total) * sizeof(*st->index_entries)); + if (!mem) + return; + st->index_entries = mem; + st->index_entries_allocated_size = (st->nb_index_entries + total) * sizeof(*st->index_entries); + + // populate index + for (i = 0; i < sc->chunk_count; i++) { + current_offset = sc->chunk_offsets[i]; + if (stsc_index + 1 < sc->stsc_count && + i + 1 == sc->stsc_data[stsc_index + 1].first) + stsc_index++; + chunk_samples = sc->stsc_data[stsc_index].count; + + while (chunk_samples > 0) { + AVIndexEntry *e; + unsigned size, samples; + + if (sc->samples_per_frame >= 160) { // gsm + samples = sc->samples_per_frame; + size = sc->bytes_per_frame; + } else { + if (sc->samples_per_frame > 1) { + samples = FFMIN((1024 / sc->samples_per_frame)* + sc->samples_per_frame, chunk_samples); + size = (samples / sc->samples_per_frame) * sc->bytes_per_frame; + } else { + samples = FFMIN(1024, chunk_samples); + size = samples * sc->sample_size; + } + } + + if (st->nb_index_entries >= total) { + av_log(mov->fc, AV_LOG_ERROR, "wrong chunk count %d\n", total); + return; + } + e = &st->index_entries[st->nb_index_entries++]; + e->pos = current_offset; + e->timestamp = current_dts; + e->size = size; + e->min_distance = 0; + e->flags = AVINDEX_KEYFRAME; + av_dlog(mov->fc, "AVIndex stream %d, chunk %d, offset %"PRIx64", dts %"PRId64", " + "size %d, duration %d\n", st->index, i, current_offset, current_dts, + size, samples); + + current_offset += size; + current_dts += samples; + chunk_samples -= samples; + } + } + } +} + +static int mov_open_dref(AVIOContext **pb, const char *src, MOVDref *ref, + AVIOInterruptCB *int_cb, int use_absolute_path, AVFormatContext *fc) +{ + /* try relative path, we do not try the absolute because it can leak information about our + system to an attacker */ + if (ref->nlvl_to > 0 && ref->nlvl_from > 0) { + char filename[1024]; + const char *src_path; + int i, l; + + /* find a source dir */ + src_path = strrchr(src, '/'); + if (src_path) + src_path++; + else + src_path = src; + + /* find a next level down to target */ + for (i = 0, l = strlen(ref->path) - 1; l >= 0; l--) + if (ref->path[l] == '/') { + if (i == ref->nlvl_to - 1) + break; + else + i++; + } + + /* compose filename if next level down to target was found */ + if (i == ref->nlvl_to - 1 && src_path - src < sizeof(filename)) { + memcpy(filename, src, src_path - src); + filename[src_path - src] = 0; + + for (i = 1; i < ref->nlvl_from; i++) + av_strlcat(filename, "../", 1024); + + av_strlcat(filename, ref->path + l + 1, 1024); + + if (!avio_open2(pb, filename, AVIO_FLAG_READ, int_cb, NULL)) + return 0; + } + } else if (use_absolute_path) { + av_log(fc, AV_LOG_WARNING, "Using absolute path on user request, " + "this is a possible security issue\n"); + if (!avio_open2(pb, ref->path, AVIO_FLAG_READ, int_cb, NULL)) + return 0; + } + + return AVERROR(ENOENT); +} + +static void fix_timescale(MOVContext *c, MOVStreamContext *sc) +{ + if (sc->time_scale <= 0) { + av_log(c->fc, AV_LOG_WARNING, "stream %d, timescale not set\n", sc->ffindex); + sc->time_scale = c->time_scale; + if (sc->time_scale <= 0) + sc->time_scale = 1; + } +} + +static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + int ret; + + st = avformat_new_stream(c->fc, NULL); + if (!st) return AVERROR(ENOMEM); + st->id = c->fc->nb_streams; + sc = av_mallocz(sizeof(MOVStreamContext)); + if (!sc) return AVERROR(ENOMEM); + + st->priv_data = sc; + st->codec->codec_type = AVMEDIA_TYPE_DATA; + sc->ffindex = st->index; + + if ((ret = mov_read_default(c, pb, atom)) < 0) + return ret; + + /* sanity checks */ + if (sc->chunk_count && (!sc->stts_count || !sc->stsc_count || + (!sc->sample_size && !sc->sample_count))) { + av_log(c->fc, AV_LOG_ERROR, "stream %d, missing mandatory atoms, broken header\n", + st->index); + return 0; + } + + fix_timescale(c, sc); + + avpriv_set_pts_info(st, 64, 1, sc->time_scale); + + mov_build_index(c, st); + + if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) { + MOVDref *dref = &sc->drefs[sc->dref_id - 1]; + if (mov_open_dref(&sc->pb, c->fc->filename, dref, &c->fc->interrupt_callback, + c->use_absolute_path, c->fc) < 0) + av_log(c->fc, AV_LOG_ERROR, + "stream %d, error opening alias: path='%s', dir='%s', " + "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n", + st->index, dref->path, dref->dir, dref->filename, + dref->volume, dref->nlvl_from, dref->nlvl_to); + } else { + sc->pb = c->fc->pb; + sc->pb_is_copied = 1; + } + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!st->sample_aspect_ratio.num && + (st->codec->width != sc->width || st->codec->height != sc->height)) { + st->sample_aspect_ratio = av_d2q(((double)st->codec->height * sc->width) / + ((double)st->codec->width * sc->height), INT_MAX); + } + + if (st->duration > 0) + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + sc->time_scale*st->nb_frames, st->duration, INT_MAX); + +#if FF_API_R_FRAME_RATE + if (sc->stts_count == 1 || (sc->stts_count == 2 && sc->stts_data[1].count == 1)) + av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, + sc->time_scale, sc->stts_data[0].duration, INT_MAX); +#endif + } + + // done for ai5q, ai52, ai55, ai1q, ai12 and ai15. + if (!st->codec->extradata_size && st->codec->codec_id == AV_CODEC_ID_H264 && + st->codec->codec_tag != MKTAG('a', 'v', 'c', '1')) { + ff_generate_avci_extradata(st); + } + + switch (st->codec->codec_id) { +#if CONFIG_H261_DECODER + case AV_CODEC_ID_H261: +#endif +#if CONFIG_H263_DECODER + case AV_CODEC_ID_H263: +#endif +#if CONFIG_MPEG4_DECODER + case AV_CODEC_ID_MPEG4: +#endif + st->codec->width = 0; /* let decoder init width/height */ + st->codec->height= 0; + break; + } + + /* Do not need those anymore. */ + av_freep(&sc->chunk_offsets); + av_freep(&sc->stsc_data); + av_freep(&sc->sample_sizes); + av_freep(&sc->keyframes); + av_freep(&sc->stts_data); + av_freep(&sc->stps_data); + av_freep(&sc->rap_group); + + return 0; +} + +static int mov_read_ilst(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int ret; + c->itunes_metadata = 1; + ret = mov_read_default(c, pb, atom); + c->itunes_metadata = 0; + return ret; +} + +static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + while (atom.size > 8) { + uint32_t tag = avio_rl32(pb); + atom.size -= 4; + if (tag == MKTAG('h','d','l','r')) { + avio_seek(pb, -8, SEEK_CUR); + atom.size += 8; + return mov_read_default(c, pb, atom); + } + } + return 0; +} + +static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int i; + int width; + int height; + int64_t disp_transform[2]; + int display_matrix[3][2]; + AVStream *st; + MOVStreamContext *sc; + int version; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + version = avio_r8(pb); + avio_rb24(pb); /* flags */ + /* + MOV_TRACK_ENABLED 0x0001 + MOV_TRACK_IN_MOVIE 0x0002 + MOV_TRACK_IN_PREVIEW 0x0004 + MOV_TRACK_IN_POSTER 0x0008 + */ + + if (version == 1) { + avio_rb64(pb); + avio_rb64(pb); + } else { + avio_rb32(pb); /* creation time */ + avio_rb32(pb); /* modification time */ + } + st->id = (int)avio_rb32(pb); /* track id (NOT 0 !)*/ + avio_rb32(pb); /* reserved */ + + /* highlevel (considering edits) duration in movie timebase */ + (version == 1) ? avio_rb64(pb) : avio_rb32(pb); + avio_rb32(pb); /* reserved */ + avio_rb32(pb); /* reserved */ + + avio_rb16(pb); /* layer */ + avio_rb16(pb); /* alternate group */ + avio_rb16(pb); /* volume */ + avio_rb16(pb); /* reserved */ + + //read in the display matrix (outlined in ISO 14496-12, Section 6.2.2) + // they're kept in fixed point format through all calculations + // ignore u,v,z b/c we don't need the scale factor to calc aspect ratio + for (i = 0; i < 3; i++) { + display_matrix[i][0] = avio_rb32(pb); // 16.16 fixed point + display_matrix[i][1] = avio_rb32(pb); // 16.16 fixed point + avio_rb32(pb); // 2.30 fixed point (not used) + } + + width = avio_rb32(pb); // 16.16 fixed point track width + height = avio_rb32(pb); // 16.16 fixed point track height + sc->width = width >> 16; + sc->height = height >> 16; + + //Assign clockwise rotate values based on transform matrix so that + //we can compensate for iPhone orientation during capture. + + if (display_matrix[1][0] == -65536 && display_matrix[0][1] == 65536) { + av_dict_set(&st->metadata, "rotate", "90", 0); + } + + if (display_matrix[0][0] == -65536 && display_matrix[1][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "180", 0); + } + + if (display_matrix[1][0] == 65536 && display_matrix[0][1] == -65536) { + av_dict_set(&st->metadata, "rotate", "270", 0); + } + + // transform the display width/height according to the matrix + // skip this if the display matrix is the default identity matrix + // or if it is rotating the picture, ex iPhone 3GS + // to keep the same scale, use [width height 1<<16] + if (width && height && + ((display_matrix[0][0] != 65536 || + display_matrix[1][1] != 65536) && + !display_matrix[0][1] && + !display_matrix[1][0] && + !display_matrix[2][0] && !display_matrix[2][1])) { + for (i = 0; i < 2; i++) + disp_transform[i] = + (int64_t) width * display_matrix[0][i] + + (int64_t) height * display_matrix[1][i] + + ((int64_t) display_matrix[2][i] << 16); + + //sample aspect ratio is new width/height divided by old width/height + st->sample_aspect_ratio = av_d2q( + ((double) disp_transform[0] * height) / + ((double) disp_transform[1] * width), INT_MAX); + } + return 0; +} + +static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVFragment *frag = &c->fragment; + MOVTrackExt *trex = NULL; + int flags, track_id, i; + + avio_r8(pb); /* version */ + flags = avio_rb24(pb); + + track_id = avio_rb32(pb); + if (!track_id) + return AVERROR_INVALIDDATA; + frag->track_id = track_id; + for (i = 0; i < c->trex_count; i++) + if (c->trex_data[i].track_id == frag->track_id) { + trex = &c->trex_data[i]; + break; + } + if (!trex) { + av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n"); + return AVERROR_INVALIDDATA; + } + + frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ? + avio_rb64(pb) : frag->moof_offset; + frag->stsd_id = flags & MOV_TFHD_STSD_ID ? avio_rb32(pb) : trex->stsd_id; + + frag->duration = flags & MOV_TFHD_DEFAULT_DURATION ? + avio_rb32(pb) : trex->duration; + frag->size = flags & MOV_TFHD_DEFAULT_SIZE ? + avio_rb32(pb) : trex->size; + frag->flags = flags & MOV_TFHD_DEFAULT_FLAGS ? + avio_rb32(pb) : trex->flags; + av_dlog(c->fc, "frag flags 0x%x\n", frag->flags); + return 0; +} + +static int mov_read_chap(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + c->chapter_track = avio_rb32(pb); + return 0; +} + +static int mov_read_trex(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVTrackExt *trex; + + if ((uint64_t)c->trex_count+1 >= UINT_MAX / sizeof(*c->trex_data)) + return AVERROR_INVALIDDATA; + trex = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data)); + if (!trex) + return AVERROR(ENOMEM); + + c->fc->duration = AV_NOPTS_VALUE; // the duration from mvhd is not representing the whole file when fragments are used. + + c->trex_data = trex; + trex = &c->trex_data[c->trex_count++]; + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + trex->track_id = avio_rb32(pb); + trex->stsd_id = avio_rb32(pb); + trex->duration = avio_rb32(pb); + trex->size = avio_rb32(pb); + trex->flags = avio_rb32(pb); + return 0; +} + +static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVFragment *frag = &c->fragment; + AVStream *st = NULL; + MOVStreamContext *sc; + MOVStts *ctts_data; + uint64_t offset; + int64_t dts; + int data_offset = 0; + unsigned entries, first_sample_flags = frag->flags; + int flags, distance, i, found_keyframe = 0; + + for (i = 0; i < c->fc->nb_streams; i++) { + if (c->fc->streams[i]->id == frag->track_id) { + st = c->fc->streams[i]; + break; + } + } + if (!st) { + av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %d\n", frag->track_id); + return AVERROR_INVALIDDATA; + } + sc = st->priv_data; + if (sc->pseudo_stream_id+1 != frag->stsd_id) + return 0; + avio_r8(pb); /* version */ + flags = avio_rb24(pb); + entries = avio_rb32(pb); + av_dlog(c->fc, "flags 0x%x entries %d\n", flags, entries); + + /* Always assume the presence of composition time offsets. + * Without this assumption, for instance, we cannot deal with a track in fragmented movies that meet the following. + * 1) in the initial movie, there are no samples. + * 2) in the first movie fragment, there is only one sample without composition time offset. + * 3) in the subsequent movie fragments, there are samples with composition time offset. */ + if (!sc->ctts_count && sc->sample_count) + { + /* Complement ctts table if moov atom doesn't have ctts atom. */ + ctts_data = av_malloc(sizeof(*sc->ctts_data)); + if (!ctts_data) + return AVERROR(ENOMEM); + sc->ctts_data = ctts_data; + sc->ctts_data[sc->ctts_count].count = sc->sample_count; + sc->ctts_data[sc->ctts_count].duration = 0; + sc->ctts_count++; + } + if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data)) + return AVERROR_INVALIDDATA; + ctts_data = av_realloc(sc->ctts_data, + (entries+sc->ctts_count)*sizeof(*sc->ctts_data)); + if (!ctts_data) + return AVERROR(ENOMEM); + sc->ctts_data = ctts_data; + + if (flags & MOV_TRUN_DATA_OFFSET) data_offset = avio_rb32(pb); + if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) first_sample_flags = avio_rb32(pb); + dts = sc->track_end - sc->time_offset; + offset = frag->base_data_offset + data_offset; + distance = 0; + av_dlog(c->fc, "first sample flags 0x%x\n", first_sample_flags); + for (i = 0; i < entries && !pb->eof_reached; i++) { + unsigned sample_size = frag->size; + int sample_flags = i ? frag->flags : first_sample_flags; + unsigned sample_duration = frag->duration; + int keyframe = 0; + + if (flags & MOV_TRUN_SAMPLE_DURATION) sample_duration = avio_rb32(pb); + if (flags & MOV_TRUN_SAMPLE_SIZE) sample_size = avio_rb32(pb); + if (flags & MOV_TRUN_SAMPLE_FLAGS) sample_flags = avio_rb32(pb); + sc->ctts_data[sc->ctts_count].count = 1; + sc->ctts_data[sc->ctts_count].duration = (flags & MOV_TRUN_SAMPLE_CTS) ? + avio_rb32(pb) : 0; + sc->ctts_count++; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + keyframe = 1; + else if (!found_keyframe) + keyframe = found_keyframe = + !(sample_flags & (MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC | + MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES)); + if (keyframe) + distance = 0; + av_add_index_entry(st, offset, dts, sample_size, distance, + keyframe ? AVINDEX_KEYFRAME : 0); + av_dlog(c->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", " + "size %d, distance %d, keyframe %d\n", st->index, sc->sample_count+i, + offset, dts, sample_size, distance, keyframe); + distance++; + dts += sample_duration; + offset += sample_size; + sc->data_size += sample_size; + } + + if (pb->eof_reached) + return AVERROR_EOF; + + frag->moof_offset = offset; + st->duration = sc->track_end = dts + sc->time_offset; + return 0; +} + +/* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */ +/* like the files created with Adobe Premiere 5.0, for samples see */ +/* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */ +static int mov_read_wide(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int err; + + if (atom.size < 8) + return 0; /* continue */ + if (avio_rb32(pb) != 0) { /* 0 sized mdat atom... use the 'wide' atom size */ + avio_skip(pb, atom.size - 4); + return 0; + } + atom.type = avio_rl32(pb); + atom.size -= 8; + if (atom.type != MKTAG('m','d','a','t')) { + avio_skip(pb, atom.size); + return 0; + } + err = mov_read_mdat(c, pb, atom); + return err; +} + +static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ +#if CONFIG_ZLIB + AVIOContext ctx; + uint8_t *cmov_data; + uint8_t *moov_data; /* uncompressed data */ + long cmov_len, moov_len; + int ret = -1; + + avio_rb32(pb); /* dcom atom */ + if (avio_rl32(pb) != MKTAG('d','c','o','m')) + return AVERROR_INVALIDDATA; + if (avio_rl32(pb) != MKTAG('z','l','i','b')) { + av_log(c->fc, AV_LOG_ERROR, "unknown compression for cmov atom !\n"); + return AVERROR_INVALIDDATA; + } + avio_rb32(pb); /* cmvd atom */ + if (avio_rl32(pb) != MKTAG('c','m','v','d')) + return AVERROR_INVALIDDATA; + moov_len = avio_rb32(pb); /* uncompressed size */ + cmov_len = atom.size - 6 * 4; + + cmov_data = av_malloc(cmov_len); + if (!cmov_data) + return AVERROR(ENOMEM); + moov_data = av_malloc(moov_len); + if (!moov_data) { + av_free(cmov_data); + return AVERROR(ENOMEM); + } + avio_read(pb, cmov_data, cmov_len); + if (uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK) + goto free_and_return; + if (ffio_init_context(&ctx, moov_data, moov_len, 0, NULL, NULL, NULL, NULL) != 0) + goto free_and_return; + atom.type = MKTAG('m','o','o','v'); + atom.size = moov_len; + ret = mov_read_default(c, &ctx, atom); +free_and_return: + av_free(moov_data); + av_free(cmov_data); + return ret; +#else + av_log(c->fc, AV_LOG_ERROR, "this file requires zlib support compiled in\n"); + return AVERROR(ENOSYS); +#endif +} + +/* edit list atom */ +static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVStreamContext *sc; + int i, edit_count, version, edit_start_index = 0; + int unsupported = 0; + + if (c->fc->nb_streams < 1 || c->ignore_editlist) + return 0; + sc = c->fc->streams[c->fc->nb_streams-1]->priv_data; + + version = avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + edit_count = avio_rb32(pb); /* entries */ + + if ((uint64_t)edit_count*12+8 > atom.size) + return AVERROR_INVALIDDATA; + + av_dlog(c->fc, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, edit_count); + for (i=0; iempty_duration = duration; + edit_start_index = 1; + } else if (i == edit_start_index && time >= 0) + sc->start_time = time; + else + unsupported = 1; + + av_dlog(c->fc, "duration=%"PRId64" time=%"PRId64" rate=%f\n", + duration, time, rate / 65536.0); + } + + if (unsupported) + av_log(c->fc, AV_LOG_WARNING, "multiple edit list entries, " + "a/v desync might occur, patch welcome\n"); + + return 0; +} + +static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; + sc->timecode_track = avio_rb32(pb); + return 0; +} + +static const MOVParseTableEntry mov_default_parse_table[] = { +{ MKTAG('A','C','L','R'), mov_read_avid }, +{ MKTAG('A','P','R','G'), mov_read_avid }, +{ MKTAG('A','A','L','P'), mov_read_avid }, +{ MKTAG('A','R','E','S'), mov_read_avid }, +{ MKTAG('a','v','s','s'), mov_read_avss }, +{ MKTAG('c','h','p','l'), mov_read_chpl }, +{ MKTAG('c','o','6','4'), mov_read_stco }, +{ MKTAG('c','t','t','s'), mov_read_ctts }, /* composition time to sample */ +{ MKTAG('d','i','n','f'), mov_read_default }, +{ MKTAG('d','r','e','f'), mov_read_dref }, +{ MKTAG('e','d','t','s'), mov_read_default }, +{ MKTAG('e','l','s','t'), mov_read_elst }, +{ MKTAG('e','n','d','a'), mov_read_enda }, +{ MKTAG('f','i','e','l'), mov_read_fiel }, +{ MKTAG('f','t','y','p'), mov_read_ftyp }, +{ MKTAG('g','l','b','l'), mov_read_glbl }, +{ MKTAG('h','d','l','r'), mov_read_hdlr }, +{ MKTAG('i','l','s','t'), mov_read_ilst }, +{ MKTAG('j','p','2','h'), mov_read_jp2h }, +{ MKTAG('m','d','a','t'), mov_read_mdat }, +{ MKTAG('m','d','h','d'), mov_read_mdhd }, +{ MKTAG('m','d','i','a'), mov_read_default }, +{ MKTAG('m','e','t','a'), mov_read_meta }, +{ MKTAG('m','i','n','f'), mov_read_default }, +{ MKTAG('m','o','o','f'), mov_read_moof }, +{ MKTAG('m','o','o','v'), mov_read_moov }, +{ MKTAG('m','v','e','x'), mov_read_default }, +{ MKTAG('m','v','h','d'), mov_read_mvhd }, +{ MKTAG('S','M','I',' '), mov_read_svq3 }, +{ MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */ +{ MKTAG('a','v','c','C'), mov_read_glbl }, +{ MKTAG('p','a','s','p'), mov_read_pasp }, +{ MKTAG('s','t','b','l'), mov_read_default }, +{ MKTAG('s','t','c','o'), mov_read_stco }, +{ MKTAG('s','t','p','s'), mov_read_stps }, +{ MKTAG('s','t','r','f'), mov_read_strf }, +{ MKTAG('s','t','s','c'), mov_read_stsc }, +{ MKTAG('s','t','s','d'), mov_read_stsd }, /* sample description */ +{ MKTAG('s','t','s','s'), mov_read_stss }, /* sync sample */ +{ MKTAG('s','t','s','z'), mov_read_stsz }, /* sample size */ +{ MKTAG('s','t','t','s'), mov_read_stts }, +{ MKTAG('s','t','z','2'), mov_read_stsz }, /* compact sample size */ +{ MKTAG('t','k','h','d'), mov_read_tkhd }, /* track header */ +{ MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */ +{ MKTAG('t','r','a','k'), mov_read_trak }, +{ MKTAG('t','r','a','f'), mov_read_default }, +{ MKTAG('t','r','e','f'), mov_read_default }, +{ MKTAG('t','m','c','d'), mov_read_tmcd }, +{ MKTAG('c','h','a','p'), mov_read_chap }, +{ MKTAG('t','r','e','x'), mov_read_trex }, +{ MKTAG('t','r','u','n'), mov_read_trun }, +{ MKTAG('u','d','t','a'), mov_read_default }, +{ MKTAG('w','a','v','e'), mov_read_wave }, +{ MKTAG('e','s','d','s'), mov_read_esds }, +{ MKTAG('d','a','c','3'), mov_read_dac3 }, /* AC-3 info */ +{ MKTAG('d','e','c','3'), mov_read_dec3 }, /* EAC-3 info */ +{ MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */ +{ MKTAG('w','f','e','x'), mov_read_wfex }, +{ MKTAG('c','m','o','v'), mov_read_cmov }, +{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */ +{ MKTAG('d','v','c','1'), mov_read_dvc1 }, +{ MKTAG('s','b','g','p'), mov_read_sbgp }, +{ 0, NULL } +}; + +static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t total_size = 0; + MOVAtom a; + int i; + + if (atom.size < 0) + atom.size = INT64_MAX; + while (total_size + 8 <= atom.size && !url_feof(pb)) { + int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL; + a.size = atom.size; + a.type=0; + if (atom.size >= 8) { + a.size = avio_rb32(pb); + a.type = avio_rl32(pb); + if (atom.type != MKTAG('r','o','o','t') && + atom.type != MKTAG('m','o','o','v')) + { + if (a.type == MKTAG('t','r','a','k') || a.type == MKTAG('m','d','a','t')) + { + av_log(c->fc, AV_LOG_ERROR, "Broken file, trak/mdat not at top-level\n"); + avio_skip(pb, -8); + return 0; + } + } + total_size += 8; + if (a.size == 1) { /* 64 bit extended size */ + a.size = avio_rb64(pb) - 8; + total_size += 8; + } + } + av_dlog(c->fc, "type: %08x '%.4s' parent:'%.4s' sz: %"PRId64" %"PRId64" %"PRId64"\n", + a.type, (char*)&a.type, (char*)&atom.type, a.size, total_size, atom.size); + if (a.size == 0) { + a.size = atom.size - total_size + 8; + } + a.size -= 8; + if (a.size < 0) + break; + a.size = FFMIN(a.size, atom.size - total_size); + + for (i = 0; mov_default_parse_table[i].type; i++) + if (mov_default_parse_table[i].type == a.type) { + parse = mov_default_parse_table[i].parse; + break; + } + + // container is user data + if (!parse && (atom.type == MKTAG('u','d','t','a') || + atom.type == MKTAG('i','l','s','t'))) + parse = mov_read_udta_string; + + if (!parse) { /* skip leaf atoms data */ + avio_skip(pb, a.size); + } else { + int64_t start_pos = avio_tell(pb); + int64_t left; + int err = parse(c, pb, a); + if (err < 0) + return err; + if (c->found_moov && c->found_mdat && + ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) || + start_pos + a.size == avio_size(pb))) { + if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) + c->next_root_atom = start_pos + a.size; + return 0; + } + left = a.size - avio_tell(pb) + start_pos; + if (left > 0) /* skip garbage at atom end */ + avio_skip(pb, left); + else if(left < 0) { + av_log(c->fc, AV_LOG_DEBUG, "undoing overread of %"PRId64" in '%.4s'\n", -left, (char*)&a.type); + avio_seek(pb, left, SEEK_CUR); + } + } + + total_size += a.size; + } + + if (total_size < atom.size && atom.size < 0x7ffff) + avio_skip(pb, atom.size - total_size); + + return 0; +} + +static int mov_probe(AVProbeData *p) +{ + int64_t offset; + uint32_t tag; + int score = 0; + int moov_offset = -1; + + /* check file header */ + offset = 0; + for (;;) { + /* ignore invalid offset */ + if ((offset + 8) > (unsigned int)p->buf_size) + break; + tag = AV_RL32(p->buf + offset + 4); + switch(tag) { + /* check for obvious tags */ + case MKTAG('m','o','o','v'): + moov_offset = offset + 4; + case MKTAG('j','P',' ',' '): /* jpeg 2000 signature */ + case MKTAG('m','d','a','t'): + case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */ + case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */ + case MKTAG('f','t','y','p'): + if (AV_RB32(p->buf+offset) < 8 && + (AV_RB32(p->buf+offset) != 1 || + offset + 12 > (unsigned int)p->buf_size || + AV_RB64(p->buf+offset + 8) == 0)) { + score = FFMAX(score, AVPROBE_SCORE_MAX - 50); + } else { + score = AVPROBE_SCORE_MAX; + } + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + break; + /* those are more common words, so rate then a bit less */ + case MKTAG('e','d','i','w'): /* xdcam files have reverted first tags */ + case MKTAG('w','i','d','e'): + case MKTAG('f','r','e','e'): + case MKTAG('j','u','n','k'): + case MKTAG('p','i','c','t'): + score = FFMAX(score, AVPROBE_SCORE_MAX - 5); + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + break; + case MKTAG(0x82,0x82,0x7f,0x7d): + case MKTAG('s','k','i','p'): + case MKTAG('u','u','i','d'): + case MKTAG('p','r','f','l'): + /* if we only find those cause probedata is too small at least rate them */ + score = FFMAX(score, AVPROBE_SCORE_MAX - 50); + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + break; + default: + offset = FFMAX(4, AV_RB32(p->buf+offset)) + offset; + } + } + if(score > AVPROBE_SCORE_MAX - 50 && moov_offset != -1) { + /* moov atom in the header - we should make sure that this is not a + * MOV-packed MPEG-PS */ + offset = moov_offset; + + while(offset < (p->buf_size - 16)){ /* Sufficient space */ + /* We found an actual hdlr atom */ + if(AV_RL32(p->buf + offset ) == MKTAG('h','d','l','r') && + AV_RL32(p->buf + offset + 8) == MKTAG('m','h','l','r') && + AV_RL32(p->buf + offset + 12) == MKTAG('M','P','E','G')){ + av_log(NULL, AV_LOG_WARNING, "Found media data tag MPEG indicating this is a MOV-packed MPEG-PS.\n"); + /* We found a media handler reference atom describing an + * MPEG-PS-in-MOV, return a + * low score to force expanding the probe window until + * mpegps_probe finds what it needs */ + return 5; + }else + /* Keep looking */ + offset+=2; + } + } + + return score; +} + +// must be done after parsing all trak because there's no order requirement +static void mov_read_chapters(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + AVStream *st = NULL; + MOVStreamContext *sc; + int64_t cur_pos; + int i; + + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->id == mov->chapter_track) { + st = s->streams[i]; + break; + } + if (!st) { + av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n"); + return; + } + + st->discard = AVDISCARD_ALL; + sc = st->priv_data; + cur_pos = avio_tell(sc->pb); + + for (i = 0; i < st->nb_index_entries; i++) { + AVIndexEntry *sample = &st->index_entries[i]; + int64_t end = i+1 < st->nb_index_entries ? st->index_entries[i+1].timestamp : st->duration; + uint8_t *title; + uint16_t ch; + int len, title_len; + + if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { + av_log(s, AV_LOG_ERROR, "Chapter %d not found in file\n", i); + goto finish; + } + + // the first two bytes are the length of the title + len = avio_rb16(sc->pb); + if (len > sample->size-2) + continue; + title_len = 2*len + 1; + if (!(title = av_mallocz(title_len))) + goto finish; + + // The samples could theoretically be in any encoding if there's an encd + // atom following, but in practice are only utf-8 or utf-16, distinguished + // instead by the presence of a BOM + if (!len) { + title[0] = 0; + } else { + ch = avio_rb16(sc->pb); + if (ch == 0xfeff) + avio_get_str16be(sc->pb, len, title, title_len); + else if (ch == 0xfffe) + avio_get_str16le(sc->pb, len, title, title_len); + else { + AV_WB16(title, ch); + if (len == 1 || len == 2) + title[len] = 0; + else + avio_get_str(sc->pb, INT_MAX, title + 2, len - 1); + } + } + + avpriv_new_chapter(s, i, st->time_base, sample->timestamp, end, title); + av_freep(&title); + } +finish: + avio_seek(sc->pb, cur_pos, SEEK_SET); +} + +static int parse_timecode_in_framenum_format(AVFormatContext *s, AVStream *st, + uint32_t value, int flags) +{ + AVTimecode tc; + char buf[AV_TIMECODE_STR_SIZE]; + AVRational rate = {st->codec->time_base.den, + st->codec->time_base.num}; + int ret = av_timecode_init(&tc, rate, flags, 0, s); + if (ret < 0) + return ret; + av_dict_set(&st->metadata, "timecode", + av_timecode_make_string(&tc, buf, value), 0); + return 0; +} + +static int mov_read_timecode_track(AVFormatContext *s, AVStream *st) +{ + MOVStreamContext *sc = st->priv_data; + int flags = 0; + int64_t cur_pos = avio_tell(sc->pb); + uint32_t value; + + if (!st->nb_index_entries) + return -1; + + avio_seek(sc->pb, st->index_entries->pos, SEEK_SET); + value = avio_rb32(s->pb); + + if (sc->tmcd_flags & 0x0001) flags |= AV_TIMECODE_FLAG_DROPFRAME; + if (sc->tmcd_flags & 0x0002) flags |= AV_TIMECODE_FLAG_24HOURSMAX; + if (sc->tmcd_flags & 0x0004) flags |= AV_TIMECODE_FLAG_ALLOWNEGATIVE; + + /* Assume Counter flag is set to 1 in tmcd track (even though it is likely + * not the case) and thus assume "frame number format" instead of QT one. + * No sample with tmcd track can be found with a QT timecode at the moment, + * despite what the tmcd track "suggests" (Counter flag set to 0 means QT + * format). */ + parse_timecode_in_framenum_format(s, st, value, flags); + + avio_seek(sc->pb, cur_pos, SEEK_SET); + return 0; +} + +static int mov_read_close(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + int i, j; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + + av_freep(&sc->ctts_data); + for (j = 0; j < sc->drefs_count; j++) { + av_freep(&sc->drefs[j].path); + av_freep(&sc->drefs[j].dir); + } + av_freep(&sc->drefs); + if (!sc->pb_is_copied) + avio_close(sc->pb); + sc->pb = NULL; + av_freep(&sc->chunk_offsets); + av_freep(&sc->keyframes); + av_freep(&sc->sample_sizes); + av_freep(&sc->stps_data); + av_freep(&sc->stsc_data); + av_freep(&sc->stts_data); + } + + if (mov->dv_demux) { + for (i = 0; i < mov->dv_fctx->nb_streams; i++) { + av_freep(&mov->dv_fctx->streams[i]->codec); + av_freep(&mov->dv_fctx->streams[i]); + } + av_freep(&mov->dv_fctx); + av_freep(&mov->dv_demux); + } + + av_freep(&mov->trex_data); + + return 0; +} + +static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + sc->timecode_track == tmcd_id) + return 1; + } + return 0; +} + +/* look for a tmcd track not referenced by any video track, and export it globally */ +static void export_orphan_timecode(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + if (st->codec->codec_tag == MKTAG('t','m','c','d') && + !tmcd_is_referenced(s, i + 1)) { + AVDictionaryEntry *tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) { + av_dict_set(&s->metadata, "timecode", tcr->value, 0); + break; + } + } + } +} + +static int mov_read_header(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + AVIOContext *pb = s->pb; + int i, j, err; + MOVAtom atom = { AV_RL32("root") }; + + mov->fc = s; + /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ + if (pb->seekable) + atom.size = avio_size(pb); + else + atom.size = INT64_MAX; + + /* check MOV header */ + if ((err = mov_read_default(mov, pb, atom)) < 0) { + av_log(s, AV_LOG_ERROR, "error reading header: %d\n", err); + mov_read_close(s); + return err; + } + if (!mov->found_moov) { + av_log(s, AV_LOG_ERROR, "moov atom not found\n"); + mov_read_close(s); + return AVERROR_INVALIDDATA; + } + av_dlog(mov->fc, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb)); + + if (pb->seekable) { + if (mov->chapter_track > 0) + mov_read_chapters(s); + for (i = 0; i < s->nb_streams; i++) + if (s->streams[i]->codec->codec_tag == AV_RL32("tmcd")) + mov_read_timecode_track(s, s->streams[i]); + } + + /* copy timecode metadata from tmcd tracks to the related video streams */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + if (sc->timecode_track > 0) { + AVDictionaryEntry *tcr; + int tmcd_st_id = -1; + + for (j = 0; j < s->nb_streams; j++) + if (s->streams[j]->id == sc->timecode_track) + tmcd_st_id = j; + + if (tmcd_st_id < 0 || tmcd_st_id == i) + continue; + tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0); + if (tcr) + av_dict_set(&st->metadata, "timecode", tcr->value, 0); + } + } + export_orphan_timecode(s); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + fix_timescale(mov, sc); + if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->codec_id == AV_CODEC_ID_AAC) { + st->skip_samples = sc->start_pad; + } + } + + if (mov->trex_data) { + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MOVStreamContext *sc = st->priv_data; + if (st->duration) + st->codec->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration; + } + } + + return 0; +} + +static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) +{ + AVIndexEntry *sample = NULL; + int64_t best_dts = INT64_MAX; + int i; + for (i = 0; i < s->nb_streams; i++) { + AVStream *avst = s->streams[i]; + MOVStreamContext *msc = avst->priv_data; + if (msc->pb && msc->current_sample < avst->nb_index_entries) { + AVIndexEntry *current_sample = &avst->index_entries[msc->current_sample]; + int64_t dts = av_rescale(current_sample->timestamp, AV_TIME_BASE, msc->time_scale); + av_dlog(s, "stream %d, sample %d, dts %"PRId64"\n", i, msc->current_sample, dts); + if (!sample || (!s->pb->seekable && current_sample->pos < sample->pos) || + (s->pb->seekable && + ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && + ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) || + (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))))) { + sample = current_sample; + best_dts = dts; + *st = avst; + } + } + } + return sample; +} + +static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MOVContext *mov = s->priv_data; + MOVStreamContext *sc; + AVIndexEntry *sample; + AVStream *st = NULL; + int ret; + mov->fc = s; + retry: + sample = mov_find_next_sample(s, &st); + if (!sample) { + mov->found_mdat = 0; + if (!mov->next_root_atom) + return AVERROR_EOF; + avio_seek(s->pb, mov->next_root_atom, SEEK_SET); + mov->next_root_atom = 0; + if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 || + url_feof(s->pb)) + return AVERROR_EOF; + av_dlog(s, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb)); + goto retry; + } + sc = st->priv_data; + /* must be done just before reading, to avoid infinite loop on sample */ + sc->current_sample++; + + if (st->discard != AVDISCARD_ALL) { + if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { + av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n", + sc->ffindex, sample->pos); + return AVERROR_INVALIDDATA; + } + ret = av_get_packet(sc->pb, pkt, sample->size); + if (ret < 0) + return ret; + if (sc->has_palette) { + uint8_t *pal; + + pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + if (!pal) { + av_log(mov->fc, AV_LOG_ERROR, "Cannot append palette to packet\n"); + } else { + memcpy(pal, sc->palette, AVPALETTE_SIZE); + sc->has_palette = 0; + } + } +#if CONFIG_DV_DEMUXER + if (mov->dv_demux && sc->dv_audio_container) { + avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size, pkt->pos); + av_free(pkt->data); + pkt->size = 0; + ret = avpriv_dv_get_packet(mov->dv_demux, pkt); + if (ret < 0) + return ret; + } +#endif + } + + pkt->stream_index = sc->ffindex; + pkt->dts = sample->timestamp; + if (sc->ctts_data && sc->ctts_index < sc->ctts_count) { + pkt->pts = pkt->dts + sc->dts_shift + sc->ctts_data[sc->ctts_index].duration; + /* update ctts context */ + sc->ctts_sample++; + if (sc->ctts_index < sc->ctts_count && + sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) { + sc->ctts_index++; + sc->ctts_sample = 0; + } + if (sc->wrong_dts) + pkt->dts = AV_NOPTS_VALUE; + } else { + int64_t next_dts = (sc->current_sample < st->nb_index_entries) ? + st->index_entries[sc->current_sample].timestamp : st->duration; + pkt->duration = next_dts - pkt->dts; + pkt->pts = pkt->dts; + } + if (st->discard == AVDISCARD_ALL) + goto retry; + pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; + pkt->pos = sample->pos; + av_dlog(s, "stream %d, pts %"PRId64", dts %"PRId64", pos 0x%"PRIx64", duration %d\n", + pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, pkt->duration); + return 0; +} + +static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags) +{ + MOVStreamContext *sc = st->priv_data; + int sample, time_sample; + int i; + + sample = av_index_search_timestamp(st, timestamp, flags); + av_dlog(s, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample); + if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp) + sample = 0; + if (sample < 0) /* not sure what to do */ + return AVERROR_INVALIDDATA; + sc->current_sample = sample; + av_dlog(s, "stream %d, found sample %d\n", st->index, sc->current_sample); + /* adjust ctts index */ + if (sc->ctts_data) { + time_sample = 0; + for (i = 0; i < sc->ctts_count; i++) { + int next = time_sample + sc->ctts_data[i].count; + if (next > sc->current_sample) { + sc->ctts_index = i; + sc->ctts_sample = sc->current_sample - time_sample; + break; + } + time_sample = next; + } + } + return sample; +} + +static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags) +{ + AVStream *st; + int64_t seek_timestamp, timestamp; + int sample; + int i; + + if (stream_index >= s->nb_streams) + return AVERROR_INVALIDDATA; + + st = s->streams[stream_index]; + sample = mov_seek_stream(s, st, sample_time, flags); + if (sample < 0) + return sample; + + /* adjust seek timestamp to found sample timestamp */ + seek_timestamp = st->index_entries[sample].timestamp; + + for (i = 0; i < s->nb_streams; i++) { + MOVStreamContext *sc = s->streams[i]->priv_data; + st = s->streams[i]; + st->skip_samples = (sample_time <= 0) ? sc->start_pad : 0; + + if (stream_index == i) + continue; + + timestamp = av_rescale_q(seek_timestamp, s->streams[stream_index]->time_base, st->time_base); + mov_seek_stream(s, st, timestamp, flags); + } + return 0; +} + +static const AVOption options[] = { + {"use_absolute_path", + "allow using absolute path when opening alias, this is a possible security issue", + offsetof(MOVContext, use_absolute_path), FF_OPT_TYPE_INT, {.i64 = 0}, + 0, 1, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_DECODING_PARAM}, + {"ignore_editlist", "", offsetof(MOVContext, ignore_editlist), FF_OPT_TYPE_INT, {.i64 = 0}, + 0, 1, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_DECODING_PARAM}, + {NULL} +}; + +static const AVClass class = { + .class_name = "mov,mp4,m4a,3gp,3g2,mj2", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_mov_demuxer = { + .name = "mov,mp4,m4a,3gp,3g2,mj2", + .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), + .priv_data_size = sizeof(MOVContext), + .read_probe = mov_probe, + .read_header = mov_read_header, + .read_packet = mov_read_packet, + .read_close = mov_read_close, + .read_seek = mov_read_seek, + .priv_class = &class, + .flags = AVFMT_NO_BYTE_SEEK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mov.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,30 @@ +libavformat/mov.o libavformat/mov.o: libavformat/mov.c \ + libavutil/attributes.h libavutil/channel_layout.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat.h libavutil/avstring.h libavutil/dict.h \ + libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavutil/timecode.h libavcodec/ac3tab.h \ + libavutil/common.h libavcodec/ac3.h libavutil/opt.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavcodec/ac3tab.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/riff.h \ + libavformat/metadata.h libavformat/isom.h libavformat/dv.h \ + libavcodec/get_bits.h libavutil/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavformat/id3v1.h libavformat/mov_chan.h \ + libavformat/qtpalette.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov.o Binary file ffmpeg/libavformat/mov.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov_chan.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mov_chan.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * mov 'chan' tag reading/writing. + * @author Justin Ruggles + */ + +#include + +#include "libavutil/channel_layout.h" +#include "libavcodec/avcodec.h" +#include "mov_chan.h" + +/** + * Channel Layout Tag + * This tells which channels are present in the audio stream and the order in + * which they appear. + * + * @note We're using the channel layout tag to indicate channel order + * when the value is greater than 0x10000. The Apple documentation has + * some contradictions as to how this is actually supposed to be handled. + * + * Core Audio File Format Spec: + * "The high 16 bits indicates a specific ordering of the channels." + * Core Audio Data Types Reference: + * "These identifiers specify the channels included in a layout but + * do not specify a particular ordering of those channels." + */ +enum MovChannelLayoutTag { + MOV_CH_LAYOUT_UNKNOWN = 0xFFFF0000, + MOV_CH_LAYOUT_USE_DESCRIPTIONS = ( 0 << 16) | 0, + MOV_CH_LAYOUT_USE_BITMAP = ( 1 << 16) | 0, + MOV_CH_LAYOUT_DISCRETEINORDER = (147 << 16) | 0, + MOV_CH_LAYOUT_MONO = (100 << 16) | 1, + MOV_CH_LAYOUT_STEREO = (101 << 16) | 2, + MOV_CH_LAYOUT_STEREOHEADPHONES = (102 << 16) | 2, + MOV_CH_LAYOUT_MATRIXSTEREO = (103 << 16) | 2, + MOV_CH_LAYOUT_MIDSIDE = (104 << 16) | 2, + MOV_CH_LAYOUT_XY = (105 << 16) | 2, + MOV_CH_LAYOUT_BINAURAL = (106 << 16) | 2, + MOV_CH_LAYOUT_AMBISONIC_B_FORMAT = (107 << 16) | 4, + MOV_CH_LAYOUT_QUADRAPHONIC = (108 << 16) | 4, + MOV_CH_LAYOUT_PENTAGONAL = (109 << 16) | 5, + MOV_CH_LAYOUT_HEXAGONAL = (110 << 16) | 6, + MOV_CH_LAYOUT_OCTAGONAL = (111 << 16) | 8, + MOV_CH_LAYOUT_CUBE = (112 << 16) | 8, + MOV_CH_LAYOUT_MPEG_3_0_A = (113 << 16) | 3, + MOV_CH_LAYOUT_MPEG_3_0_B = (114 << 16) | 3, + MOV_CH_LAYOUT_MPEG_4_0_A = (115 << 16) | 4, + MOV_CH_LAYOUT_MPEG_4_0_B = (116 << 16) | 4, + MOV_CH_LAYOUT_MPEG_5_0_A = (117 << 16) | 5, + MOV_CH_LAYOUT_MPEG_5_0_B = (118 << 16) | 5, + MOV_CH_LAYOUT_MPEG_5_0_C = (119 << 16) | 5, + MOV_CH_LAYOUT_MPEG_5_0_D = (120 << 16) | 5, + MOV_CH_LAYOUT_MPEG_5_1_A = (121 << 16) | 6, + MOV_CH_LAYOUT_MPEG_5_1_B = (122 << 16) | 6, + MOV_CH_LAYOUT_MPEG_5_1_C = (123 << 16) | 6, + MOV_CH_LAYOUT_MPEG_5_1_D = (124 << 16) | 6, + MOV_CH_LAYOUT_MPEG_6_1_A = (125 << 16) | 7, + MOV_CH_LAYOUT_MPEG_7_1_A = (126 << 16) | 8, + MOV_CH_LAYOUT_MPEG_7_1_B = (127 << 16) | 8, + MOV_CH_LAYOUT_MPEG_7_1_C = (128 << 16) | 8, + MOV_CH_LAYOUT_EMAGIC_DEFAULT_7_1 = (129 << 16) | 8, + MOV_CH_LAYOUT_SMPTE_DTV = (130 << 16) | 8, + MOV_CH_LAYOUT_ITU_2_1 = (131 << 16) | 3, + MOV_CH_LAYOUT_ITU_2_2 = (132 << 16) | 4, + MOV_CH_LAYOUT_DVD_4 = (133 << 16) | 3, + MOV_CH_LAYOUT_DVD_5 = (134 << 16) | 4, + MOV_CH_LAYOUT_DVD_6 = (135 << 16) | 5, + MOV_CH_LAYOUT_DVD_10 = (136 << 16) | 4, + MOV_CH_LAYOUT_DVD_11 = (137 << 16) | 5, + MOV_CH_LAYOUT_DVD_18 = (138 << 16) | 5, + MOV_CH_LAYOUT_AUDIOUNIT_6_0 = (139 << 16) | 6, + MOV_CH_LAYOUT_AUDIOUNIT_7_0 = (140 << 16) | 7, + MOV_CH_LAYOUT_AUDIOUNIT_7_0_FRONT = (148 << 16) | 7, + MOV_CH_LAYOUT_AAC_6_0 = (141 << 16) | 6, + MOV_CH_LAYOUT_AAC_6_1 = (142 << 16) | 7, + MOV_CH_LAYOUT_AAC_7_0 = (143 << 16) | 7, + MOV_CH_LAYOUT_AAC_OCTAGONAL = (144 << 16) | 8, + MOV_CH_LAYOUT_TMH_10_2_STD = (145 << 16) | 16, + MOV_CH_LAYOUT_TMH_10_2_FULL = (146 << 16) | 21, + MOV_CH_LAYOUT_AC3_1_0_1 = (149 << 16) | 2, + MOV_CH_LAYOUT_AC3_3_0 = (150 << 16) | 3, + MOV_CH_LAYOUT_AC3_3_1 = (151 << 16) | 4, + MOV_CH_LAYOUT_AC3_3_0_1 = (152 << 16) | 4, + MOV_CH_LAYOUT_AC3_2_1_1 = (153 << 16) | 4, + MOV_CH_LAYOUT_AC3_3_1_1 = (154 << 16) | 5, + MOV_CH_LAYOUT_EAC3_6_0_A = (155 << 16) | 6, + MOV_CH_LAYOUT_EAC3_7_0_A = (156 << 16) | 7, + MOV_CH_LAYOUT_EAC3_6_1_A = (157 << 16) | 7, + MOV_CH_LAYOUT_EAC3_6_1_B = (158 << 16) | 7, + MOV_CH_LAYOUT_EAC3_6_1_C = (159 << 16) | 7, + MOV_CH_LAYOUT_EAC3_7_1_A = (160 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_B = (161 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_C = (162 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_D = (163 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_E = (164 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_F = (165 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_G = (166 << 16) | 8, + MOV_CH_LAYOUT_EAC3_7_1_H = (167 << 16) | 8, + MOV_CH_LAYOUT_DTS_3_1 = (168 << 16) | 4, + MOV_CH_LAYOUT_DTS_4_1 = (169 << 16) | 5, + MOV_CH_LAYOUT_DTS_6_0_A = (170 << 16) | 6, + MOV_CH_LAYOUT_DTS_6_0_B = (171 << 16) | 6, + MOV_CH_LAYOUT_DTS_6_0_C = (172 << 16) | 6, + MOV_CH_LAYOUT_DTS_6_1_A = (173 << 16) | 7, + MOV_CH_LAYOUT_DTS_6_1_B = (174 << 16) | 7, + MOV_CH_LAYOUT_DTS_6_1_C = (175 << 16) | 7, + MOV_CH_LAYOUT_DTS_6_1_D = (182 << 16) | 7, + MOV_CH_LAYOUT_DTS_7_0 = (176 << 16) | 7, + MOV_CH_LAYOUT_DTS_7_1 = (177 << 16) | 8, + MOV_CH_LAYOUT_DTS_8_0_A = (178 << 16) | 8, + MOV_CH_LAYOUT_DTS_8_0_B = (179 << 16) | 8, + MOV_CH_LAYOUT_DTS_8_1_A = (180 << 16) | 9, + MOV_CH_LAYOUT_DTS_8_1_B = (181 << 16) | 9, +}; + +struct MovChannelLayoutMap { + uint32_t tag; + uint64_t layout; +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_misc[] = { + { MOV_CH_LAYOUT_USE_DESCRIPTIONS, 0 }, + { MOV_CH_LAYOUT_USE_BITMAP, 0 }, + { MOV_CH_LAYOUT_DISCRETEINORDER, 0 }, + { MOV_CH_LAYOUT_UNKNOWN, 0 }, + { MOV_CH_LAYOUT_TMH_10_2_STD, 0 }, // L, R, C, Vhc, Lsd, Rsd, + // Ls, Rs, Vhl, Vhr, Lw, Rw, + // Csd, Cs, LFE1, LFE2 + { MOV_CH_LAYOUT_TMH_10_2_FULL, 0 }, // L, R, C, Vhc, Lsd, Rsd, + // Ls, Rs, Vhl, Vhr, Lw, Rw, + // Csd, Cs, LFE1, LFE2, Lc, Rc, + // HI, VI, Haptic + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_1ch[] = { + { MOV_CH_LAYOUT_MONO, AV_CH_LAYOUT_MONO }, // C + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_2ch[] = { + { MOV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_STEREO }, // L, R + { MOV_CH_LAYOUT_STEREOHEADPHONES, AV_CH_LAYOUT_STEREO }, // L, R + { MOV_CH_LAYOUT_BINAURAL, AV_CH_LAYOUT_STEREO }, // L, R + { MOV_CH_LAYOUT_MIDSIDE, AV_CH_LAYOUT_STEREO }, // C, sides + { MOV_CH_LAYOUT_XY, AV_CH_LAYOUT_STEREO }, // X (left), Y (right) + + { MOV_CH_LAYOUT_MATRIXSTEREO, AV_CH_LAYOUT_STEREO_DOWNMIX }, // Lt, Rt + + { MOV_CH_LAYOUT_AC3_1_0_1, AV_CH_LAYOUT_MONO | // C, LFE + AV_CH_LOW_FREQUENCY }, + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_3ch[] = { + { MOV_CH_LAYOUT_MPEG_3_0_A, AV_CH_LAYOUT_SURROUND }, // L, R, C + { MOV_CH_LAYOUT_MPEG_3_0_B, AV_CH_LAYOUT_SURROUND }, // C, L, R + { MOV_CH_LAYOUT_AC3_3_0, AV_CH_LAYOUT_SURROUND }, // L, C, R + + { MOV_CH_LAYOUT_ITU_2_1, AV_CH_LAYOUT_2_1 }, // L, R, Cs + + { MOV_CH_LAYOUT_DVD_4, AV_CH_LAYOUT_2POINT1 }, // L, R, LFE + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_4ch[] = { + { MOV_CH_LAYOUT_AMBISONIC_B_FORMAT, 0 }, // W, X, Y, Z + + { MOV_CH_LAYOUT_QUADRAPHONIC, AV_CH_LAYOUT_QUAD }, // L, R, Rls, Rrs + + { MOV_CH_LAYOUT_MPEG_4_0_A, AV_CH_LAYOUT_4POINT0 }, // L, R, C, Cs + { MOV_CH_LAYOUT_MPEG_4_0_B, AV_CH_LAYOUT_4POINT0 }, // C, L, R, Cs + { MOV_CH_LAYOUT_AC3_3_1, AV_CH_LAYOUT_4POINT0 }, // L, C, R, Cs + + { MOV_CH_LAYOUT_ITU_2_2, AV_CH_LAYOUT_2_2 }, // L, R, Ls, Rs + + { MOV_CH_LAYOUT_DVD_5, AV_CH_LAYOUT_2_1 | // L, R, LFE, Cs + AV_CH_LOW_FREQUENCY }, + { MOV_CH_LAYOUT_AC3_2_1_1, AV_CH_LAYOUT_2_1 | // L, R, Cs, LFE + AV_CH_LOW_FREQUENCY }, + + { MOV_CH_LAYOUT_DVD_10, AV_CH_LAYOUT_3POINT1 }, // L, R, C, LFE + { MOV_CH_LAYOUT_AC3_3_0_1, AV_CH_LAYOUT_3POINT1 }, // L, C, R, LFE + { MOV_CH_LAYOUT_DTS_3_1, AV_CH_LAYOUT_3POINT1 }, // C, L, R, LFE + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_5ch[] = { + { MOV_CH_LAYOUT_PENTAGONAL, AV_CH_LAYOUT_5POINT0_BACK }, // L, R, Rls, Rrs, C + + { MOV_CH_LAYOUT_MPEG_5_0_A, AV_CH_LAYOUT_5POINT0 }, // L, R, C, Ls, Rs + { MOV_CH_LAYOUT_MPEG_5_0_B, AV_CH_LAYOUT_5POINT0 }, // L, R, Ls, Rs, C + { MOV_CH_LAYOUT_MPEG_5_0_C, AV_CH_LAYOUT_5POINT0 }, // L, C, R, Ls, Rs + { MOV_CH_LAYOUT_MPEG_5_0_D, AV_CH_LAYOUT_5POINT0 }, // C, L, R, Ls, Rs + + { MOV_CH_LAYOUT_DVD_6, AV_CH_LAYOUT_2_2 | // L, R, LFE, Ls, Rs + AV_CH_LOW_FREQUENCY }, + { MOV_CH_LAYOUT_DVD_18, AV_CH_LAYOUT_2_2 | // L, R, Ls, Rs, LFE + AV_CH_LOW_FREQUENCY }, + + { MOV_CH_LAYOUT_DVD_11, AV_CH_LAYOUT_4POINT1 }, // L, R, C, LFE, Cs + { MOV_CH_LAYOUT_AC3_3_1_1, AV_CH_LAYOUT_4POINT1 }, // L, C, R, Cs, LFE + { MOV_CH_LAYOUT_DTS_4_1, AV_CH_LAYOUT_4POINT1 }, // C, L, R, Cs, LFE + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_6ch[] = { + { MOV_CH_LAYOUT_HEXAGONAL, AV_CH_LAYOUT_HEXAGONAL }, // L, R, Rls, Rrs, C, Cs + { MOV_CH_LAYOUT_DTS_6_0_C, AV_CH_LAYOUT_HEXAGONAL }, // C, Cs, L, R, Rls, Rrs + + { MOV_CH_LAYOUT_MPEG_5_1_A, AV_CH_LAYOUT_5POINT1 }, // L, R, C, LFE, Ls, Rs + { MOV_CH_LAYOUT_MPEG_5_1_B, AV_CH_LAYOUT_5POINT1 }, // L, R, Ls, Rs, C, LFE + { MOV_CH_LAYOUT_MPEG_5_1_C, AV_CH_LAYOUT_5POINT1 }, // L, C, R, Ls, Rs, LFE + { MOV_CH_LAYOUT_MPEG_5_1_D, AV_CH_LAYOUT_5POINT1 }, // C, L, R, Ls, Rs, LFE + + { MOV_CH_LAYOUT_AUDIOUNIT_6_0, AV_CH_LAYOUT_6POINT0 }, // L, R, Ls, Rs, C, Cs + { MOV_CH_LAYOUT_AAC_6_0, AV_CH_LAYOUT_6POINT0 }, // C, L, R, Ls, Rs, Cs + { MOV_CH_LAYOUT_EAC3_6_0_A, AV_CH_LAYOUT_6POINT0 }, // L, C, R, Ls, Rs, Cs + + { MOV_CH_LAYOUT_DTS_6_0_A, AV_CH_LAYOUT_6POINT0_FRONT }, // Lc, Rc, L, R, Ls, Rs + + { MOV_CH_LAYOUT_DTS_6_0_B, AV_CH_LAYOUT_5POINT0_BACK | // C, L, R, Rls, Rrs, Ts + AV_CH_TOP_CENTER }, + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_7ch[] = { + { MOV_CH_LAYOUT_MPEG_6_1_A, AV_CH_LAYOUT_6POINT1 }, // L, R, C, LFE, Ls, Rs, Cs + { MOV_CH_LAYOUT_AAC_6_1, AV_CH_LAYOUT_6POINT1 }, // C, L, R, Ls, Rs, Cs, LFE + { MOV_CH_LAYOUT_EAC3_6_1_A, AV_CH_LAYOUT_6POINT1 }, // L, C, R, Ls, Rs, LFE, Cs + { MOV_CH_LAYOUT_DTS_6_1_D, AV_CH_LAYOUT_6POINT1 }, // C, L, R, Ls, Rs, LFE, Cs + + { MOV_CH_LAYOUT_AUDIOUNIT_7_0, AV_CH_LAYOUT_7POINT0 }, // L, R, Ls, Rs, C, Rls, Rrs + { MOV_CH_LAYOUT_AAC_7_0, AV_CH_LAYOUT_7POINT0 }, // C, L, R, Ls, Rs, Rls, Rrs + { MOV_CH_LAYOUT_EAC3_7_0_A, AV_CH_LAYOUT_7POINT0 }, // L, C, R, Ls, Rs, Rls, Rrs + + { MOV_CH_LAYOUT_AUDIOUNIT_7_0_FRONT, AV_CH_LAYOUT_7POINT0_FRONT }, // L, R, Ls, Rs, C, Lc, Rc + { MOV_CH_LAYOUT_DTS_7_0, AV_CH_LAYOUT_7POINT0_FRONT }, // Lc, C, Rc, L, R, Ls, Rs + + { MOV_CH_LAYOUT_EAC3_6_1_B, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Ts + AV_CH_TOP_CENTER }, + + { MOV_CH_LAYOUT_EAC3_6_1_C, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Vhc + AV_CH_TOP_FRONT_CENTER }, + + { MOV_CH_LAYOUT_DTS_6_1_A, AV_CH_LAYOUT_6POINT1_FRONT }, // Lc, Rc, L, R, Ls, Rs, LFE + + { MOV_CH_LAYOUT_DTS_6_1_B, AV_CH_LAYOUT_5POINT1_BACK | // C, L, R, Rls, Rrs, Ts, LFE + AV_CH_TOP_CENTER }, + + { MOV_CH_LAYOUT_DTS_6_1_C, AV_CH_LAYOUT_6POINT1_BACK }, // C, Cs, L, R, Rls, Rrs, LFE + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_8ch[] = { + { MOV_CH_LAYOUT_OCTAGONAL, AV_CH_LAYOUT_OCTAGONAL }, // L, R, Rls, Rrs, C, Cs, Ls, Rs + { MOV_CH_LAYOUT_AAC_OCTAGONAL, AV_CH_LAYOUT_OCTAGONAL }, // C, L, R, Ls, Rs, Rls, Rrs, Cs + + { MOV_CH_LAYOUT_CUBE, AV_CH_LAYOUT_QUAD | // L, R, Rls, Rrs, Vhl, Vhr, Rlt, Rrt + AV_CH_TOP_FRONT_LEFT | + AV_CH_TOP_FRONT_RIGHT | + AV_CH_TOP_BACK_LEFT | + AV_CH_TOP_BACK_RIGHT }, + + { MOV_CH_LAYOUT_MPEG_7_1_A, AV_CH_LAYOUT_7POINT1_WIDE }, // L, R, C, LFE, Ls, Rs, Lc, Rc + { MOV_CH_LAYOUT_MPEG_7_1_B, AV_CH_LAYOUT_7POINT1_WIDE }, // C, Lc, Rc, L, R, Ls, Rs, LFE + { MOV_CH_LAYOUT_EMAGIC_DEFAULT_7_1, AV_CH_LAYOUT_7POINT1_WIDE }, // L, R, Ls, Rs, C, LFE, Lc, Rc + { MOV_CH_LAYOUT_EAC3_7_1_B, AV_CH_LAYOUT_7POINT1_WIDE }, // L, C, R, Ls, Rs, LFE, Lc, Rc + { MOV_CH_LAYOUT_DTS_7_1, AV_CH_LAYOUT_7POINT1_WIDE }, // Lc, C, Rc, L, R, Ls, Rs, LFE + + { MOV_CH_LAYOUT_MPEG_7_1_C, AV_CH_LAYOUT_7POINT1 }, // L, R, C, LFE, Ls, Rs, Rls, Rrs + { MOV_CH_LAYOUT_EAC3_7_1_A, AV_CH_LAYOUT_7POINT1 }, // L, C, R, Ls, Rs, LFE, Rls, Rrs + + { MOV_CH_LAYOUT_SMPTE_DTV, AV_CH_LAYOUT_5POINT1 | // L, R, C, LFE, Ls, Rs, Lt, Rt + AV_CH_LAYOUT_STEREO_DOWNMIX }, + + { MOV_CH_LAYOUT_EAC3_7_1_C, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Lsd, Rsd + AV_CH_SURROUND_DIRECT_LEFT | + AV_CH_SURROUND_DIRECT_RIGHT }, + + { MOV_CH_LAYOUT_EAC3_7_1_D, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Lw, Rw + AV_CH_WIDE_LEFT | + AV_CH_WIDE_RIGHT }, + + { MOV_CH_LAYOUT_EAC3_7_1_E, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Vhl, Vhr + AV_CH_TOP_FRONT_LEFT | + AV_CH_TOP_FRONT_RIGHT }, + + { MOV_CH_LAYOUT_EAC3_7_1_F, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Cs, Ts + AV_CH_BACK_CENTER | + AV_CH_TOP_CENTER }, + + { MOV_CH_LAYOUT_EAC3_7_1_G, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Cs, Vhc + AV_CH_BACK_CENTER | + AV_CH_TOP_FRONT_CENTER }, + + { MOV_CH_LAYOUT_EAC3_7_1_H, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Ts, Vhc + AV_CH_TOP_CENTER | + AV_CH_TOP_FRONT_CENTER }, + + { MOV_CH_LAYOUT_DTS_8_0_A, AV_CH_LAYOUT_2_2 | // Lc, Rc, L, R, Ls, Rs, Rls, Rrs + AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | + AV_CH_FRONT_LEFT_OF_CENTER | + AV_CH_FRONT_RIGHT_OF_CENTER }, + + { MOV_CH_LAYOUT_DTS_8_0_B, AV_CH_LAYOUT_5POINT0 | // Lc, C, Rc, L, R, Ls, Cs, Rs + AV_CH_FRONT_LEFT_OF_CENTER | + AV_CH_FRONT_RIGHT_OF_CENTER | + AV_CH_BACK_CENTER }, + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap mov_ch_layout_map_9ch[] = { + { MOV_CH_LAYOUT_DTS_8_1_A, AV_CH_LAYOUT_2_2 | // Lc, Rc, L, R, Ls, Rs, Rls, Rrs, LFE + AV_CH_BACK_LEFT | + AV_CH_BACK_RIGHT | + AV_CH_FRONT_LEFT_OF_CENTER | + AV_CH_FRONT_RIGHT_OF_CENTER | + AV_CH_LOW_FREQUENCY }, + + { MOV_CH_LAYOUT_DTS_8_1_B, AV_CH_LAYOUT_7POINT1_WIDE | // Lc, C, Rc, L, R, Ls, Cs, Rs, LFE + AV_CH_BACK_CENTER }, + { 0, 0 }, +}; + +static const struct MovChannelLayoutMap *mov_ch_layout_map[] = { + mov_ch_layout_map_misc, + mov_ch_layout_map_1ch, + mov_ch_layout_map_2ch, + mov_ch_layout_map_3ch, + mov_ch_layout_map_4ch, + mov_ch_layout_map_5ch, + mov_ch_layout_map_6ch, + mov_ch_layout_map_7ch, + mov_ch_layout_map_8ch, + mov_ch_layout_map_9ch, +}; + +static const enum MovChannelLayoutTag mov_ch_layouts_aac[] = { + MOV_CH_LAYOUT_MONO, + MOV_CH_LAYOUT_STEREO, + MOV_CH_LAYOUT_AC3_1_0_1, + MOV_CH_LAYOUT_MPEG_3_0_B, + MOV_CH_LAYOUT_ITU_2_1, + MOV_CH_LAYOUT_DVD_4, + MOV_CH_LAYOUT_QUADRAPHONIC, + MOV_CH_LAYOUT_MPEG_4_0_B, + MOV_CH_LAYOUT_ITU_2_2, + MOV_CH_LAYOUT_AC3_2_1_1, + MOV_CH_LAYOUT_DTS_3_1, + MOV_CH_LAYOUT_MPEG_5_0_D, + MOV_CH_LAYOUT_DVD_18, + MOV_CH_LAYOUT_DTS_4_1, + MOV_CH_LAYOUT_MPEG_5_1_D, + MOV_CH_LAYOUT_AAC_6_0, + MOV_CH_LAYOUT_DTS_6_0_A, + MOV_CH_LAYOUT_AAC_6_1, + MOV_CH_LAYOUT_AAC_7_0, + MOV_CH_LAYOUT_DTS_6_1_A, + MOV_CH_LAYOUT_AAC_OCTAGONAL, + MOV_CH_LAYOUT_MPEG_7_1_B, + MOV_CH_LAYOUT_DTS_8_0_A, + 0, +}; + +static const enum MovChannelLayoutTag mov_ch_layouts_ac3[] = { + MOV_CH_LAYOUT_MONO, + MOV_CH_LAYOUT_STEREO, + MOV_CH_LAYOUT_AC3_1_0_1, + MOV_CH_LAYOUT_AC3_3_0, + MOV_CH_LAYOUT_ITU_2_1, + MOV_CH_LAYOUT_DVD_4, + MOV_CH_LAYOUT_AC3_3_1, + MOV_CH_LAYOUT_ITU_2_2, + MOV_CH_LAYOUT_AC3_2_1_1, + MOV_CH_LAYOUT_AC3_3_0_1, + MOV_CH_LAYOUT_MPEG_5_0_C, + MOV_CH_LAYOUT_DVD_18, + MOV_CH_LAYOUT_AC3_3_1_1, + MOV_CH_LAYOUT_MPEG_5_1_C, + 0, +}; + +static const enum MovChannelLayoutTag mov_ch_layouts_alac[] = { + MOV_CH_LAYOUT_MONO, + MOV_CH_LAYOUT_STEREO, + MOV_CH_LAYOUT_MPEG_3_0_B, + MOV_CH_LAYOUT_MPEG_4_0_B, + MOV_CH_LAYOUT_MPEG_5_0_D, + MOV_CH_LAYOUT_MPEG_5_1_D, + MOV_CH_LAYOUT_AAC_6_1, + MOV_CH_LAYOUT_MPEG_7_1_B, + 0, +}; + +static const enum MovChannelLayoutTag mov_ch_layouts_wav[] = { + MOV_CH_LAYOUT_MONO, + MOV_CH_LAYOUT_STEREO, + MOV_CH_LAYOUT_MATRIXSTEREO, + MOV_CH_LAYOUT_MPEG_3_0_A, + MOV_CH_LAYOUT_QUADRAPHONIC, + MOV_CH_LAYOUT_MPEG_5_0_A, + MOV_CH_LAYOUT_MPEG_5_1_A, + MOV_CH_LAYOUT_MPEG_6_1_A, + MOV_CH_LAYOUT_MPEG_7_1_A, + MOV_CH_LAYOUT_MPEG_7_1_C, + MOV_CH_LAYOUT_SMPTE_DTV, + 0, +}; + +static const struct { + enum AVCodecID codec_id; + const enum MovChannelLayoutTag *layouts; +} mov_codec_ch_layouts[] = { + { AV_CODEC_ID_AAC, mov_ch_layouts_aac }, + { AV_CODEC_ID_AC3, mov_ch_layouts_ac3 }, + { AV_CODEC_ID_ALAC, mov_ch_layouts_alac }, + { AV_CODEC_ID_PCM_U8, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S8, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S16LE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S16BE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S24LE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S24BE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S32LE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_S32BE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_F32LE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_F32BE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_F64LE, mov_ch_layouts_wav }, + { AV_CODEC_ID_PCM_F64BE, mov_ch_layouts_wav }, + { AV_CODEC_ID_NONE, NULL }, +}; + +uint64_t ff_mov_get_channel_layout(uint32_t tag, uint32_t bitmap) +{ + int i, channels; + const struct MovChannelLayoutMap *layout_map; + + /* use ff_mov_get_channel_label() to build a layout instead */ + if (tag == MOV_CH_LAYOUT_USE_DESCRIPTIONS) + return 0; + + /* handle the use of the channel bitmap */ + if (tag == MOV_CH_LAYOUT_USE_BITMAP) + return bitmap < 0x40000 ? bitmap : 0; + + /* get the layout map based on the channel count for the specified layout tag */ + channels = tag & 0xFFFF; + if (channels > 9) + channels = 0; + layout_map = mov_ch_layout_map[channels]; + + /* find the channel layout for the specified layout tag */ + for (i = 0; layout_map[i].tag != 0; i++) { + if (layout_map[i].tag == tag) + break; + } + return layout_map[i].layout; +} + +static uint32_t mov_get_channel_label(uint32_t label) +{ + if (label == 0) + return 0; + if (label <= 18) + return 1U << (label - 1); + if (label == 38) + return AV_CH_STEREO_LEFT; + if (label == 39) + return AV_CH_STEREO_RIGHT; + return 0; +} + +uint32_t ff_mov_get_channel_layout_tag(enum AVCodecID codec_id, + uint64_t channel_layout, + uint32_t *bitmap) +{ + int i, j; + uint32_t tag = 0; + const enum MovChannelLayoutTag *layouts = NULL; + + /* find the layout list for the specified codec */ + for (i = 0; mov_codec_ch_layouts[i].codec_id != AV_CODEC_ID_NONE; i++) { + if (mov_codec_ch_layouts[i].codec_id == codec_id) + break; + } + if (mov_codec_ch_layouts[i].codec_id != AV_CODEC_ID_NONE) + layouts = mov_codec_ch_layouts[i].layouts; + + if (layouts) { + int channels; + const struct MovChannelLayoutMap *layout_map; + + /* get the layout map based on the channel count */ + channels = av_get_channel_layout_nb_channels(channel_layout); + if (channels > 9) + channels = 0; + layout_map = mov_ch_layout_map[channels]; + + /* find the layout tag for the specified channel layout */ + for (i = 0; layouts[i] != 0; i++) { + if ((layouts[i] & 0xFFFF) != channels) + continue; + for (j = 0; layout_map[j].tag != 0; j++) { + if (layout_map[j].tag == layouts[i] && + layout_map[j].layout == channel_layout) + break; + } + if (layout_map[j].tag) + break; + } + tag = layouts[i]; + } + + /* if no tag was found, use channel bitmap as a backup if possible */ + if (tag == 0 && channel_layout > 0 && channel_layout < 0x40000) { + tag = MOV_CH_LAYOUT_USE_BITMAP; + *bitmap = (uint32_t)channel_layout; + } else + *bitmap = 0; + + /* TODO: set channel descriptions as a secondary backup */ + + return tag; +} + +int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, + int64_t size) +{ + uint32_t layout_tag, bitmap, num_descr, label_mask; + int i; + + if (size < 12) + return AVERROR_INVALIDDATA; + + layout_tag = avio_rb32(pb); + bitmap = avio_rb32(pb); + num_descr = avio_rb32(pb); + + av_dlog(s, "chan: layout=%u bitmap=%u num_descr=%u\n", + layout_tag, bitmap, num_descr); + + if (size < 12ULL + num_descr * 20ULL) + return 0; + + label_mask = 0; + for (i = 0; i < num_descr; i++) { + uint32_t label; + label = avio_rb32(pb); // mChannelLabel + avio_rb32(pb); // mChannelFlags + avio_rl32(pb); // mCoordinates[0] + avio_rl32(pb); // mCoordinates[1] + avio_rl32(pb); // mCoordinates[2] + size -= 20; + if (layout_tag == 0) { + uint32_t mask_incr = mov_get_channel_label(label); + if (mask_incr == 0) { + label_mask = 0; + break; + } + label_mask |= mask_incr; + } + } + if (layout_tag == 0) { + if (label_mask) + st->codec->channel_layout = label_mask; + } else + st->codec->channel_layout = ff_mov_get_channel_layout(layout_tag, bitmap); + avio_skip(pb, size - 12); + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov_chan.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mov_chan.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/mov_chan.o libavformat/mov_chan.o: libavformat/mov_chan.c \ + libavutil/channel_layout.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavformat/mov_chan.h \ + libavformat/avformat.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov_chan.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mov_chan.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Justin Ruggles + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * mov 'chan' tag reading/writing. + * @author Justin Ruggles + */ + +#ifndef AVFORMAT_MOV_CHAN_H +#define AVFORMAT_MOV_CHAN_H + +#include + +#include "libavcodec/avcodec.h" +#include "avformat.h" + +/** + * Get the channel layout for the specified channel layout tag. + * + * @param[in] tag channel layout tag + * @param[out] bitmap channel bitmap (only used if needed) + * @return channel layout + */ +uint64_t ff_mov_get_channel_layout(uint32_t tag, uint32_t bitmap); + +/** + * Get the channel layout tag for the specified codec id and channel layout. + * If the layout tag was not found, use a channel bitmap if possible. + * + * @param[in] codec_id codec id + * @param[in] channel_layout channel layout + * @param[out] bitmap channel bitmap + * @return channel layout tag + */ +uint32_t ff_mov_get_channel_layout_tag(enum AVCodecID codec_id, + uint64_t channel_layout, + uint32_t *bitmap); + +/** + * Read 'chan' tag from the input stream. + * + * @param s AVFormatContext + * @param pb AVIOContext + * @param st The stream to set codec values for + * @param size Remaining size in the 'chan' tag + * @return 0 if ok, or negative AVERROR code on failure + */ +int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, + int64_t size); + +#endif /* AVFORMAT_MOV_CHAN_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mov_chan.o Binary file ffmpeg/libavformat/mov_chan.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/movenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,4088 @@ +/* + * MOV, 3GP, MP4 muxer + * Copyright (c) 2003 Thomas Raivio + * Copyright (c) 2004 Gildas Bazin + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "movenc.h" +#include "avformat.h" +#include "avio_internal.h" +#include "riff.h" +#include "avio.h" +#include "isom.h" +#include "avc.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" +#include "libavcodec/vc1.h" +#include "internal.h" +#include "libavutil/avstring.h" +#include "libavutil/intfloat.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/dict.h" +#include "rtpenc.h" +#include "mov_chan.h" + +#undef NDEBUG +#include + +static const AVOption options[] = { + { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "moov_size", "maximum moov size so it can be placed at the begin", offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 }, + { "empty_moov", "Make the initial moov atom empty (not supported by QuickTime)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "faststart", "Run a second pass to put the moov at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags), + { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +#define MOV_CLASS(flavor)\ +static const AVClass flavor ## _muxer_class = {\ + .class_name = #flavor " muxer",\ + .item_name = av_default_item_name,\ + .option = options,\ + .version = LIBAVUTIL_VERSION_INT,\ +}; + +//FIXME support 64 bit variant with wide placeholders +static int64_t update_size(AVIOContext *pb, int64_t pos) +{ + int64_t curpos = avio_tell(pb); + avio_seek(pb, pos, SEEK_SET); + avio_wb32(pb, curpos - pos); /* rewrite size */ + avio_seek(pb, curpos, SEEK_SET); + + return curpos - pos; +} + +static int supports_edts(MOVMuxContext *mov) +{ + // EDTS with fragments is tricky as we dont know the duration when its written + return (mov->use_editlist<0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) || mov->use_editlist>0; +} + +static int is_co64_required(const MOVTrack *track) +{ + int i; + + for (i = 0; i < track->entry; i++) { + if (!track->cluster[i].chunkNum) + continue; + if (track->cluster[i].pos + track->data_offset > UINT32_MAX) + return 1; + } + return 0; +} + +/* Chunk offset atom */ +static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track) +{ + int i; + int mode64 = is_co64_required(track); // use 32 bit size variant if possible + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + if (mode64) + ffio_wfourcc(pb, "co64"); + else + ffio_wfourcc(pb, "stco"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb32(pb, track->chunkCount); /* entry count */ + for (i=0; ientry; i++) { + if(!track->cluster[i].chunkNum) + continue; + if(mode64 == 1) + avio_wb64(pb, track->cluster[i].pos + track->data_offset); + else + avio_wb32(pb, track->cluster[i].pos + track->data_offset); + } + return update_size(pb, pos); +} + +/* Sample size atom */ +static int mov_write_stsz_tag(AVIOContext *pb, MOVTrack *track) +{ + int equalChunks = 1; + int i, j, entries = 0, tst = -1, oldtst = -1; + + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "stsz"); + avio_wb32(pb, 0); /* version & flags */ + + for (i=0; ientry; i++) { + tst = track->cluster[i].size/track->cluster[i].entries; + if(oldtst != -1 && tst != oldtst) { + equalChunks = 0; + } + oldtst = tst; + entries += track->cluster[i].entries; + } + if (equalChunks && track->entry) { + int sSize = track->entry ? track->cluster[0].size/track->cluster[0].entries : 0; + sSize = FFMAX(1, sSize); // adpcm mono case could make sSize == 0 + avio_wb32(pb, sSize); // sample size + avio_wb32(pb, entries); // sample count + } + else { + avio_wb32(pb, 0); // sample size + avio_wb32(pb, entries); // sample count + for (i=0; ientry; i++) { + for (j=0; jcluster[i].entries; j++) { + avio_wb32(pb, track->cluster[i].size / + track->cluster[i].entries); + } + } + } + return update_size(pb, pos); +} + +/* Sample to chunk atom */ +static int mov_write_stsc_tag(AVIOContext *pb, MOVTrack *track) +{ + int index = 0, oldval = -1, i; + int64_t entryPos, curpos; + + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "stsc"); + avio_wb32(pb, 0); // version & flags + entryPos = avio_tell(pb); + avio_wb32(pb, track->chunkCount); // entry count + for (i=0; ientry; i++) { + if (oldval != track->cluster[i].samples_in_chunk && track->cluster[i].chunkNum) + { + avio_wb32(pb, track->cluster[i].chunkNum); // first chunk + avio_wb32(pb, track->cluster[i].samples_in_chunk); // samples per chunk + avio_wb32(pb, 0x1); // sample description index + oldval = track->cluster[i].samples_in_chunk; + index++; + } + } + curpos = avio_tell(pb); + avio_seek(pb, entryPos, SEEK_SET); + avio_wb32(pb, index); // rewrite size + avio_seek(pb, curpos, SEEK_SET); + + return update_size(pb, pos); +} + +/* Sync sample atom */ +static int mov_write_stss_tag(AVIOContext *pb, MOVTrack *track, uint32_t flag) +{ + int64_t curpos, entryPos; + int i, index = 0; + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); // size + ffio_wfourcc(pb, flag == MOV_SYNC_SAMPLE ? "stss" : "stps"); + avio_wb32(pb, 0); // version & flags + entryPos = avio_tell(pb); + avio_wb32(pb, track->entry); // entry count + for (i=0; ientry; i++) { + if (track->cluster[i].flags & flag) { + avio_wb32(pb, i+1); + index++; + } + } + curpos = avio_tell(pb); + avio_seek(pb, entryPos, SEEK_SET); + avio_wb32(pb, index); // rewrite size + avio_seek(pb, curpos, SEEK_SET); + return update_size(pb, pos); +} + +static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track) +{ + avio_wb32(pb, 0x11); /* size */ + if (track->mode == MODE_MOV) ffio_wfourcc(pb, "samr"); + else ffio_wfourcc(pb, "damr"); + ffio_wfourcc(pb, "FFMP"); + avio_w8(pb, 0); /* decoder version */ + + avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ + avio_w8(pb, 0x00); /* Mode change period (no restriction) */ + avio_w8(pb, 0x01); /* Frames per sample */ + return 0x11; +} + +static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track) +{ + GetBitContext gbc; + PutBitContext pbc; + uint8_t buf[3]; + int fscod, bsid, bsmod, acmod, lfeon, frmsizecod; + + if (track->vos_len < 7) + return -1; + + avio_wb32(pb, 11); + ffio_wfourcc(pb, "dac3"); + + init_get_bits(&gbc, track->vos_data + 4, (track->vos_len - 4) * 8); + fscod = get_bits(&gbc, 2); + frmsizecod = get_bits(&gbc, 6); + bsid = get_bits(&gbc, 5); + bsmod = get_bits(&gbc, 3); + acmod = get_bits(&gbc, 3); + if (acmod == 2) { + skip_bits(&gbc, 2); // dsurmod + } else { + if ((acmod & 1) && acmod != 1) + skip_bits(&gbc, 2); // cmixlev + if (acmod & 4) + skip_bits(&gbc, 2); // surmixlev + } + lfeon = get_bits1(&gbc); + + init_put_bits(&pbc, buf, sizeof(buf)); + put_bits(&pbc, 2, fscod); + put_bits(&pbc, 5, bsid); + put_bits(&pbc, 3, bsmod); + put_bits(&pbc, 3, acmod); + put_bits(&pbc, 1, lfeon); + put_bits(&pbc, 5, frmsizecod>>1); // bit_rate_code + put_bits(&pbc, 5, 0); // reserved + + flush_put_bits(&pbc); + avio_write(pb, buf, sizeof(buf)); + + return 11; +} + +/** + * This function writes extradata "as is". + * Extradata must be formatted like a valid atom (with size and tag). + */ +static int mov_write_extradata_tag(AVIOContext *pb, MOVTrack *track) +{ + avio_write(pb, track->enc->extradata, track->enc->extradata_size); + return track->enc->extradata_size; +} + +static int mov_write_enda_tag(AVIOContext *pb) +{ + avio_wb32(pb, 10); + ffio_wfourcc(pb, "enda"); + avio_wb16(pb, 1); /* little endian */ + return 10; +} + +static int mov_write_enda_tag_be(AVIOContext *pb) +{ + avio_wb32(pb, 10); + ffio_wfourcc(pb, "enda"); + avio_wb16(pb, 0); /* big endian */ + return 10; +} + +static void put_descr(AVIOContext *pb, int tag, unsigned int size) +{ + int i = 3; + avio_w8(pb, tag); + for(; i>0; i--) + avio_w8(pb, (size>>(7*i)) | 0x80); + avio_w8(pb, size & 0x7F); +} + +static unsigned compute_avg_bitrate(MOVTrack *track) +{ + uint64_t size = 0; + int i; + if (!track->track_duration) + return 0; + for (i = 0; i < track->entry; i++) + size += track->cluster[i].size; + return size * 8 * track->timescale / track->track_duration; +} + +static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic +{ + int64_t pos = avio_tell(pb); + int decoder_specific_info_len = track->vos_len ? 5 + track->vos_len : 0; + unsigned avg_bitrate; + + avio_wb32(pb, 0); // size + ffio_wfourcc(pb, "esds"); + avio_wb32(pb, 0); // Version + + // ES descriptor + put_descr(pb, 0x03, 3 + 5+13 + decoder_specific_info_len + 5+1); + avio_wb16(pb, track->track_id); + avio_w8(pb, 0x00); // flags (= no flags) + + // DecoderConfig descriptor + put_descr(pb, 0x04, 13 + decoder_specific_info_len); + + // Object type indication + if ((track->enc->codec_id == AV_CODEC_ID_MP2 || + track->enc->codec_id == AV_CODEC_ID_MP3) && + track->enc->sample_rate > 24000) + avio_w8(pb, 0x6B); // 11172-3 + else + avio_w8(pb, ff_codec_get_tag(ff_mp4_obj_type, track->enc->codec_id)); + + // the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio) + // plus 1 bit to indicate upstream and 1 bit set to 1 (reserved) + if(track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + avio_w8(pb, 0x15); // flags (= Audiostream) + else + avio_w8(pb, 0x11); // flags (= Visualstream) + + avio_wb24(pb, track->enc->rc_buffer_size >> 3); // Buffersize DB + + avg_bitrate = compute_avg_bitrate(track); + // maxbitrate (FIXME should be max rate in any 1 sec window) + avio_wb32(pb, FFMAX3(track->enc->bit_rate, track->enc->rc_max_rate, avg_bitrate)); + avio_wb32(pb, avg_bitrate); + + if (track->vos_len) { + // DecoderSpecific info descriptor + put_descr(pb, 0x05, track->vos_len); + avio_write(pb, track->vos_data, track->vos_len); + } + + // SL descriptor + put_descr(pb, 0x06, 1); + avio_w8(pb, 0x02); + return update_size(pb, pos); +} + +static int mov_pcm_le_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24LE || + codec_id == AV_CODEC_ID_PCM_S32LE || + codec_id == AV_CODEC_ID_PCM_F32LE || + codec_id == AV_CODEC_ID_PCM_F64LE; +} + +static int mov_pcm_be_gt16(enum AVCodecID codec_id) +{ + return codec_id == AV_CODEC_ID_PCM_S24BE || + codec_id == AV_CODEC_ID_PCM_S32BE || + codec_id == AV_CODEC_ID_PCM_F32BE || + codec_id == AV_CODEC_ID_PCM_F64BE; +} + +static int mov_write_ms_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); + avio_wl32(pb, track->tag); // store it byteswapped + track->enc->codec_tag = av_bswap16(track->tag >> 16); + ff_put_wav_header(pb, track->enc); + return update_size(pb, pos); +} + +static int mov_write_wfex_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); + ffio_wfourcc(pb, "wfex"); + ff_put_wav_header(pb, track->enc); + return update_size(pb, pos); +} + +static int mov_write_chan_tag(AVIOContext *pb, MOVTrack *track) +{ + uint32_t layout_tag, bitmap; + int64_t pos = avio_tell(pb); + + layout_tag = ff_mov_get_channel_layout_tag(track->enc->codec_id, + track->enc->channel_layout, + &bitmap); + if (!layout_tag) { + av_log(track->enc, AV_LOG_WARNING, "not writing 'chan' tag due to " + "lack of channel information\n"); + return 0; + } + + avio_wb32(pb, 0); // Size + ffio_wfourcc(pb, "chan"); // Type + avio_w8(pb, 0); // Version + avio_wb24(pb, 0); // Flags + avio_wb32(pb, layout_tag); // mChannelLayoutTag + avio_wb32(pb, bitmap); // mChannelBitmap + avio_wb32(pb, 0); // mNumberChannelDescriptions + + return update_size(pb, pos); +} + +static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "wave"); + + if (track->enc->codec_id != AV_CODEC_ID_QDM2) { + avio_wb32(pb, 12); /* size */ + ffio_wfourcc(pb, "frma"); + avio_wl32(pb, track->tag); + } + + if (track->enc->codec_id == AV_CODEC_ID_AAC) { + /* useless atom needed by mplayer, ipod, not needed by quicktime */ + avio_wb32(pb, 12); /* size */ + ffio_wfourcc(pb, "mp4a"); + avio_wb32(pb, 0); + mov_write_esds_tag(pb, track); + } else if (mov_pcm_le_gt16(track->enc->codec_id)) { + mov_write_enda_tag(pb); + } else if (mov_pcm_be_gt16(track->enc->codec_id)) { + mov_write_enda_tag_be(pb); + } else if (track->enc->codec_id == AV_CODEC_ID_AMR_NB) { + mov_write_amr_tag(pb, track); + } else if (track->enc->codec_id == AV_CODEC_ID_AC3) { + mov_write_ac3_tag(pb, track); + } else if (track->enc->codec_id == AV_CODEC_ID_ALAC || + track->enc->codec_id == AV_CODEC_ID_QDM2) { + mov_write_extradata_tag(pb, track); + } else if (track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || + track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { + mov_write_ms_tag(pb, track); + } + + avio_wb32(pb, 8); /* size */ + avio_wb32(pb, 0); /* null tag */ + + return update_size(pb, pos); +} + +static int mov_write_dvc1_structs(MOVTrack *track, uint8_t *buf) +{ + uint8_t *unescaped; + const uint8_t *start, *next, *end = track->vos_data + track->vos_len; + int unescaped_size, seq_found = 0; + int level = 0, interlace = 0; + int packet_seq = track->vc1_info.packet_seq; + int packet_entry = track->vc1_info.packet_entry; + int slices = track->vc1_info.slices; + PutBitContext pbc; + + if (track->start_dts == AV_NOPTS_VALUE) { + /* No packets written yet, vc1_info isn't authoritative yet. */ + /* Assume inline sequence and entry headers. This will be + * overwritten at the end if the file is seekable. */ + packet_seq = packet_entry = 1; + } + + unescaped = av_mallocz(track->vos_len + FF_INPUT_BUFFER_PADDING_SIZE); + if (!unescaped) + return AVERROR(ENOMEM); + start = find_next_marker(track->vos_data, end); + for (next = start; next < end; start = next) { + GetBitContext gb; + int size; + next = find_next_marker(start + 4, end); + size = next - start - 4; + if (size <= 0) + continue; + unescaped_size = vc1_unescape_buffer(start + 4, size, unescaped); + init_get_bits(&gb, unescaped, 8 * unescaped_size); + if (AV_RB32(start) == VC1_CODE_SEQHDR) { + int profile = get_bits(&gb, 2); + if (profile != PROFILE_ADVANCED) { + av_free(unescaped); + return AVERROR(ENOSYS); + } + seq_found = 1; + level = get_bits(&gb, 3); + /* chromaformat, frmrtq_postproc, bitrtq_postproc, postprocflag, + * width, height */ + skip_bits_long(&gb, 2 + 3 + 5 + 1 + 2*12); + skip_bits(&gb, 1); /* broadcast */ + interlace = get_bits1(&gb); + skip_bits(&gb, 4); /* tfcntrflag, finterpflag, reserved, psf */ + } + } + if (!seq_found) { + av_free(unescaped); + return AVERROR(ENOSYS); + } + + init_put_bits(&pbc, buf, 7); + /* VC1DecSpecStruc */ + put_bits(&pbc, 4, 12); /* profile - advanced */ + put_bits(&pbc, 3, level); + put_bits(&pbc, 1, 0); /* reserved */ + /* VC1AdvDecSpecStruc */ + put_bits(&pbc, 3, level); + put_bits(&pbc, 1, 0); /* cbr */ + put_bits(&pbc, 6, 0); /* reserved */ + put_bits(&pbc, 1, !interlace); /* no interlace */ + put_bits(&pbc, 1, !packet_seq); /* no multiple seq */ + put_bits(&pbc, 1, !packet_entry); /* no multiple entry */ + put_bits(&pbc, 1, !slices); /* no slice code */ + put_bits(&pbc, 1, 0); /* no bframe */ + put_bits(&pbc, 1, 0); /* reserved */ + put_bits32(&pbc, track->enc->time_base.den); /* framerate */ + flush_put_bits(&pbc); + + av_free(unescaped); + + return 0; +} + +static int mov_write_dvc1_tag(AVIOContext *pb, MOVTrack *track) +{ + uint8_t buf[7] = { 0 }; + int ret; + + if ((ret = mov_write_dvc1_structs(track, buf)) < 0) + return ret; + + avio_wb32(pb, track->vos_len + 8 + sizeof(buf)); + ffio_wfourcc(pb, "dvc1"); + track->vc1_info.struct_offset = avio_tell(pb); + avio_write(pb, buf, sizeof(buf)); + avio_write(pb, track->vos_data, track->vos_len); + + return 0; +} + +static int mov_write_glbl_tag(AVIOContext *pb, MOVTrack *track) +{ + avio_wb32(pb, track->vos_len + 8); + ffio_wfourcc(pb, "glbl"); + avio_write(pb, track->vos_data, track->vos_len); + return 8 + track->vos_len; +} + +/** + * Compute flags for 'lpcm' tag. + * See CoreAudioTypes and AudioStreamBasicDescription at Apple. + */ +static int mov_get_lpcm_flags(enum AVCodecID codec_id) +{ + switch (codec_id) { + case AV_CODEC_ID_PCM_F32BE: + case AV_CODEC_ID_PCM_F64BE: + return 11; + case AV_CODEC_ID_PCM_F32LE: + case AV_CODEC_ID_PCM_F64LE: + return 9; + case AV_CODEC_ID_PCM_U8: + return 10; + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32BE: + return 14; + case AV_CODEC_ID_PCM_S8: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S32LE: + return 12; + default: + return 0; + } +} + +static int get_cluster_duration(MOVTrack *track, int cluster_idx) +{ + int64_t next_dts; + + if (cluster_idx >= track->entry) + return 0; + + if (cluster_idx + 1 == track->entry) + next_dts = track->track_duration + track->start_dts; + else + next_dts = track->cluster[cluster_idx + 1].dts; + + return next_dts - track->cluster[cluster_idx].dts; +} + +static int get_samples_per_packet(MOVTrack *track) +{ + int i, first_duration; + +// return track->enc->frame_size; + + /* use 1 for raw PCM */ + if (!track->audio_vbr) + return 1; + + /* check to see if duration is constant for all clusters */ + if (!track->entry) + return 0; + first_duration = get_cluster_duration(track, 0); + for (i = 1; i < track->entry; i++) { + if (get_cluster_duration(track, i) != first_duration) + return 0; + } + return first_duration; +} + +static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int version = 0; + uint32_t tag = track->tag; + + if (track->mode == MODE_MOV) { + if (track->timescale > UINT16_MAX) { + if (mov_get_lpcm_flags(track->enc->codec_id)) + tag = AV_RL32("lpcm"); + version = 2; + } else if (track->audio_vbr || mov_pcm_le_gt16(track->enc->codec_id) || + mov_pcm_be_gt16(track->enc->codec_id) || + track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || + track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + track->enc->codec_id == AV_CODEC_ID_QDM2) { + version = 1; + } + } + + avio_wb32(pb, 0); /* size */ + avio_wl32(pb, tag); // store it byteswapped + avio_wb32(pb, 0); /* Reserved */ + avio_wb16(pb, 0); /* Reserved */ + avio_wb16(pb, 1); /* Data-reference index, XXX == 1 */ + + /* SoundDescription */ + avio_wb16(pb, version); /* Version */ + avio_wb16(pb, 0); /* Revision level */ + avio_wb32(pb, 0); /* Reserved */ + + if (version == 2) { + avio_wb16(pb, 3); + avio_wb16(pb, 16); + avio_wb16(pb, 0xfffe); + avio_wb16(pb, 0); + avio_wb32(pb, 0x00010000); + avio_wb32(pb, 72); + avio_wb64(pb, av_double2int(track->enc->sample_rate)); + avio_wb32(pb, track->enc->channels); + avio_wb32(pb, 0x7F000000); + avio_wb32(pb, av_get_bits_per_sample(track->enc->codec_id)); + avio_wb32(pb, mov_get_lpcm_flags(track->enc->codec_id)); + avio_wb32(pb, track->sample_size); + avio_wb32(pb, get_samples_per_packet(track)); + } else { + if (track->mode == MODE_MOV) { + avio_wb16(pb, track->enc->channels); + if (track->enc->codec_id == AV_CODEC_ID_PCM_U8 || + track->enc->codec_id == AV_CODEC_ID_PCM_S8) + avio_wb16(pb, 8); /* bits per sample */ + else + avio_wb16(pb, 16); + avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */ + } else { /* reserved for mp4/3gp */ + avio_wb16(pb, 2); + avio_wb16(pb, 16); + avio_wb16(pb, 0); + } + + avio_wb16(pb, 0); /* packet size (= 0) */ + avio_wb16(pb, track->enc->sample_rate <= UINT16_MAX ? + track->enc->sample_rate : 0); + avio_wb16(pb, 0); /* Reserved */ + } + + if(version == 1) { /* SoundDescription V1 extended info */ + if (mov_pcm_le_gt16(track->enc->codec_id) || + mov_pcm_be_gt16(track->enc->codec_id)) + avio_wb32(pb, 1); /* must be 1 for uncompressed formats */ + else + avio_wb32(pb, track->enc->frame_size); /* Samples per packet */ + avio_wb32(pb, track->sample_size / track->enc->channels); /* Bytes per packet */ + avio_wb32(pb, track->sample_size); /* Bytes per frame */ + avio_wb32(pb, 2); /* Bytes per sample */ + } + + if(track->mode == MODE_MOV && + (track->enc->codec_id == AV_CODEC_ID_AAC || + track->enc->codec_id == AV_CODEC_ID_AC3 || + track->enc->codec_id == AV_CODEC_ID_AMR_NB || + track->enc->codec_id == AV_CODEC_ID_ALAC || + track->enc->codec_id == AV_CODEC_ID_ADPCM_MS || + track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + track->enc->codec_id == AV_CODEC_ID_QDM2 || + (mov_pcm_le_gt16(track->enc->codec_id) && version==1) || + (mov_pcm_be_gt16(track->enc->codec_id) && version==1))) + mov_write_wave_tag(pb, track); + else if(track->tag == MKTAG('m','p','4','a')) + mov_write_esds_tag(pb, track); + else if(track->enc->codec_id == AV_CODEC_ID_AMR_NB) + mov_write_amr_tag(pb, track); + else if(track->enc->codec_id == AV_CODEC_ID_AC3) + mov_write_ac3_tag(pb, track); + else if(track->enc->codec_id == AV_CODEC_ID_ALAC) + mov_write_extradata_tag(pb, track); + else if (track->enc->codec_id == AV_CODEC_ID_WMAPRO) + mov_write_wfex_tag(pb, track); + else if (track->vos_len > 0) + mov_write_glbl_tag(pb, track); + + if (track->mode == MODE_MOV && track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + mov_write_chan_tag(pb, track); + + return update_size(pb, pos); +} + +static int mov_write_d263_tag(AVIOContext *pb) +{ + avio_wb32(pb, 0xf); /* size */ + ffio_wfourcc(pb, "d263"); + ffio_wfourcc(pb, "FFMP"); + avio_w8(pb, 0); /* decoder version */ + /* FIXME use AVCodecContext level/profile, when encoder will set values */ + avio_w8(pb, 0xa); /* level */ + avio_w8(pb, 0); /* profile */ + return 0xf; +} + +static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "avcC"); + ff_isom_write_avcc(pb, track->vos_data, track->vos_len); + return update_size(pb, pos); +} + +/* also used by all avid codecs (dv, imx, meridien) and their variants */ +static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) +{ + int i; + avio_wb32(pb, 24); /* size */ + ffio_wfourcc(pb, "ACLR"); + ffio_wfourcc(pb, "ACLR"); + ffio_wfourcc(pb, "0001"); + avio_wb32(pb, 2); /* yuv range: full 1 / normal 2 */ + avio_wb32(pb, 0); /* unknown */ + + avio_wb32(pb, 24); /* size */ + ffio_wfourcc(pb, "APRG"); + ffio_wfourcc(pb, "APRG"); + ffio_wfourcc(pb, "0001"); + avio_wb32(pb, 1); /* unknown */ + avio_wb32(pb, 0); /* unknown */ + + avio_wb32(pb, 120); /* size */ + ffio_wfourcc(pb, "ARES"); + ffio_wfourcc(pb, "ARES"); + ffio_wfourcc(pb, "0001"); + avio_wb32(pb, AV_RB32(track->vos_data + 0x28)); /* dnxhd cid, some id ? */ + avio_wb32(pb, track->enc->width); + /* values below are based on samples created with quicktime and avid codecs */ + if (track->vos_data[5] & 2) { // interlaced + avio_wb32(pb, track->enc->height/2); + avio_wb32(pb, 2); /* unknown */ + avio_wb32(pb, 0); /* unknown */ + avio_wb32(pb, 4); /* unknown */ + } else { + avio_wb32(pb, track->enc->height); + avio_wb32(pb, 1); /* unknown */ + avio_wb32(pb, 0); /* unknown */ + if (track->enc->height == 1080) + avio_wb32(pb, 5); /* unknown */ + else + avio_wb32(pb, 6); /* unknown */ + } + /* padding */ + for (i = 0; i < 10; i++) + avio_wb64(pb, 0); + + /* extra padding for stsd needed */ + avio_wb32(pb, 0); + return 0; +} + +static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->enc->codec_tag; + + if (!ff_codec_get_tag(ff_mp4_obj_type, track->enc->codec_id)) + return 0; + + if (track->enc->codec_id == AV_CODEC_ID_H264) tag = MKTAG('a','v','c','1'); + else if (track->enc->codec_id == AV_CODEC_ID_AC3) tag = MKTAG('a','c','-','3'); + else if (track->enc->codec_id == AV_CODEC_ID_DIRAC) tag = MKTAG('d','r','a','c'); + else if (track->enc->codec_id == AV_CODEC_ID_MOV_TEXT) tag = MKTAG('t','x','3','g'); + else if (track->enc->codec_id == AV_CODEC_ID_VC1) tag = MKTAG('v','c','-','1'); + else if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) tag = MKTAG('m','p','4','v'); + else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) tag = MKTAG('m','p','4','a'); + + return tag; +} + +static const AVCodecTag codec_ipod_tags[] = { + { AV_CODEC_ID_H264, MKTAG('a','v','c','1') }, + { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') }, + { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') }, + { AV_CODEC_ID_ALAC, MKTAG('a','l','a','c') }, + { AV_CODEC_ID_AC3, MKTAG('a','c','-','3') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->enc->codec_tag; + + // keep original tag for subs, ipod supports both formats + if (!(track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE && + (tag == MKTAG('t','x','3','g') || + tag == MKTAG('t','e','x','t')))) + tag = ff_codec_get_tag(codec_ipod_tags, track->enc->codec_id); + + if (!av_match_ext(s->filename, "m4a") && !av_match_ext(s->filename, "m4v")) + av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v " + "Quicktime/Ipod might not play the file\n"); + + return tag; +} + +static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag; + + if (track->enc->width == 720) { /* SD */ + if (track->enc->height == 480) { /* NTSC */ + if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','n'); + else tag = MKTAG('d','v','c',' '); + }else if (track->enc->pix_fmt == AV_PIX_FMT_YUV422P) tag = MKTAG('d','v','5','p'); + else if (track->enc->pix_fmt == AV_PIX_FMT_YUV420P) tag = MKTAG('d','v','c','p'); + else tag = MKTAG('d','v','p','p'); + } else if (track->enc->height == 720) { /* HD 720 line */ + if (track->enc->time_base.den == 50) tag = MKTAG('d','v','h','q'); + else tag = MKTAG('d','v','h','p'); + } else if (track->enc->height == 1080) { /* HD 1080 line */ + if (track->enc->time_base.den == 25) tag = MKTAG('d','v','h','5'); + else tag = MKTAG('d','v','h','6'); + } else { + av_log(s, AV_LOG_ERROR, "unsupported height for dv codec\n"); + return 0; + } + + return tag; +} + +static const struct { + enum AVPixelFormat pix_fmt; + uint32_t tag; + unsigned bps; +} mov_pix_fmt_tags[] = { + { AV_PIX_FMT_YUYV422, MKTAG('y','u','v','2'), 0 }, + { AV_PIX_FMT_YUYV422, MKTAG('y','u','v','s'), 0 }, + { AV_PIX_FMT_UYVY422, MKTAG('2','v','u','y'), 0 }, + { AV_PIX_FMT_RGB555BE,MKTAG('r','a','w',' '), 16 }, + { AV_PIX_FMT_RGB555LE,MKTAG('L','5','5','5'), 16 }, + { AV_PIX_FMT_RGB565LE,MKTAG('L','5','6','5'), 16 }, + { AV_PIX_FMT_RGB565BE,MKTAG('B','5','6','5'), 16 }, + { AV_PIX_FMT_GRAY16BE,MKTAG('b','1','6','g'), 16 }, + { AV_PIX_FMT_RGB24, MKTAG('r','a','w',' '), 24 }, + { AV_PIX_FMT_BGR24, MKTAG('2','4','B','G'), 24 }, + { AV_PIX_FMT_ARGB, MKTAG('r','a','w',' '), 32 }, + { AV_PIX_FMT_BGRA, MKTAG('B','G','R','A'), 32 }, + { AV_PIX_FMT_RGBA, MKTAG('R','G','B','A'), 32 }, + { AV_PIX_FMT_ABGR, MKTAG('A','B','G','R'), 32 }, + { AV_PIX_FMT_RGB48BE, MKTAG('b','4','8','r'), 48 }, +}; + +static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->enc->codec_tag; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(mov_pix_fmt_tags); i++) { + if (track->enc->pix_fmt == mov_pix_fmt_tags[i].pix_fmt) { + tag = mov_pix_fmt_tags[i].tag; + track->enc->bits_per_coded_sample = mov_pix_fmt_tags[i].bps; + if (track->enc->codec_tag == mov_pix_fmt_tags[i].tag) + break; + } + } + + return tag; +} + +static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->enc->codec_tag; + + if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL && + (track->enc->codec_id == AV_CODEC_ID_DVVIDEO || + track->enc->codec_id == AV_CODEC_ID_RAWVIDEO || + track->enc->codec_id == AV_CODEC_ID_H263 || + av_get_bits_per_sample(track->enc->codec_id)))) { // pcm audio + if (track->enc->codec_id == AV_CODEC_ID_DVVIDEO) + tag = mov_get_dv_codec_tag(s, track); + else if (track->enc->codec_id == AV_CODEC_ID_RAWVIDEO) + tag = mov_get_rawvideo_codec_tag(s, track); + else if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { + tag = ff_codec_get_tag(ff_codec_movvideo_tags, track->enc->codec_id); + if (!tag) { // if no mac fcc found, try with Microsoft tags + tag = ff_codec_get_tag(ff_codec_bmp_tags, track->enc->codec_id); + if (tag) + av_log(s, AV_LOG_WARNING, "Using MS style video codec tag, " + "the file may be unplayable!\n"); + } + } else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) { + tag = ff_codec_get_tag(ff_codec_movaudio_tags, track->enc->codec_id); + if (!tag) { // if no mac fcc found, try with Microsoft tags + int ms_tag = ff_codec_get_tag(ff_codec_wav_tags, track->enc->codec_id); + if (ms_tag) { + tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff), (ms_tag & 0xff)); + av_log(s, AV_LOG_WARNING, "Using MS style audio codec tag, " + "the file may be unplayable!\n"); + } + } + } else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) + tag = ff_codec_get_tag(ff_codec_movsubtitle_tags, track->enc->codec_id); + } + + return tag; +} + +static const AVCodecTag codec_3gp_tags[] = { + { AV_CODEC_ID_H263, MKTAG('s','2','6','3') }, + { AV_CODEC_ID_H264, MKTAG('a','v','c','1') }, + { AV_CODEC_ID_MPEG4, MKTAG('m','p','4','v') }, + { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') }, + { AV_CODEC_ID_AMR_NB, MKTAG('s','a','m','r') }, + { AV_CODEC_ID_AMR_WB, MKTAG('s','a','w','b') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +static const AVCodecTag codec_f4v_tags[] = { // XXX: add GIF/PNG/JPEG? + { AV_CODEC_ID_MP3, MKTAG('.','m','p','3') }, + { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') }, + { AV_CODEC_ID_H264, MKTAG('a','v','c','1') }, + { AV_CODEC_ID_VP6F, MKTAG('V','P','6','F') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag; + + if (track->mode == MODE_MP4 || track->mode == MODE_PSP) + tag = mp4_get_codec_tag(s, track); + else if (track->mode == MODE_ISM) { + tag = mp4_get_codec_tag(s, track); + if (!tag && track->enc->codec_id == AV_CODEC_ID_WMAPRO) + tag = MKTAG('w', 'm', 'a', ' '); + } else if (track->mode == MODE_IPOD) + tag = ipod_get_codec_tag(s, track); + else if (track->mode & MODE_3GP) + tag = ff_codec_get_tag(codec_3gp_tags, track->enc->codec_id); + else if (track->mode & MODE_F4V) + tag = ff_codec_get_tag(codec_f4v_tags, track->enc->codec_id); + else + tag = mov_get_codec_tag(s, track); + + return tag; +} + +/** Write uuid atom. + * Needed to make file play in iPods running newest firmware + * goes after avcC atom in moov.trak.mdia.minf.stbl.stsd.avc1 + */ +static int mov_write_uuid_tag_ipod(AVIOContext *pb) +{ + avio_wb32(pb, 28); + ffio_wfourcc(pb, "uuid"); + avio_wb32(pb, 0x6b6840f2); + avio_wb32(pb, 0x5f244fc5); + avio_wb32(pb, 0xba39a51b); + avio_wb32(pb, 0xcf0323f3); + avio_wb32(pb, 0x0); + return 28; +} + +static const uint16_t fiel_data[] = { + 0x0000, 0x0100, 0x0201, 0x0206, 0x0209, 0x020e +}; + +static int mov_write_fiel_tag(AVIOContext *pb, MOVTrack *track) +{ + unsigned mov_field_order = 0; + if (track->enc->field_order < FF_ARRAY_ELEMS(fiel_data)) + mov_field_order = fiel_data[track->enc->field_order]; + else + return 0; + avio_wb32(pb, 10); + ffio_wfourcc(pb, "fiel"); + avio_wb16(pb, mov_field_order); + return 10; +} + +static int mov_write_subtitle_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + avio_wl32(pb, track->tag); // store it byteswapped + avio_wb32(pb, 0); /* Reserved */ + avio_wb16(pb, 0); /* Reserved */ + avio_wb16(pb, 1); /* Data-reference index */ + + if (track->enc->extradata_size) + avio_write(pb, track->enc->extradata, track->enc->extradata_size); + + return update_size(pb, pos); +} + +static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) +{ + AVRational sar; + av_reduce(&sar.num, &sar.den, track->enc->sample_aspect_ratio.num, + track->enc->sample_aspect_ratio.den, INT_MAX); + + avio_wb32(pb, 16); + ffio_wfourcc(pb, "pasp"); + avio_wb32(pb, sar.num); + avio_wb32(pb, sar.den); + return 16; +} + +static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + char compressor_name[32] = { 0 }; + + avio_wb32(pb, 0); /* size */ + avio_wl32(pb, track->tag); // store it byteswapped + avio_wb32(pb, 0); /* Reserved */ + avio_wb16(pb, 0); /* Reserved */ + avio_wb16(pb, 1); /* Data-reference index */ + + avio_wb16(pb, 0); /* Codec stream version */ + avio_wb16(pb, 0); /* Codec stream revision (=0) */ + if (track->mode == MODE_MOV) { + ffio_wfourcc(pb, "FFMP"); /* Vendor */ + if(track->enc->codec_id == AV_CODEC_ID_RAWVIDEO) { + avio_wb32(pb, 0); /* Temporal Quality */ + avio_wb32(pb, 0x400); /* Spatial Quality = lossless*/ + } else { + avio_wb32(pb, 0x200); /* Temporal Quality = normal */ + avio_wb32(pb, 0x200); /* Spatial Quality = normal */ + } + } else { + avio_wb32(pb, 0); /* Reserved */ + avio_wb32(pb, 0); /* Reserved */ + avio_wb32(pb, 0); /* Reserved */ + } + avio_wb16(pb, track->enc->width); /* Video width */ + avio_wb16(pb, track->height); /* Video height */ + avio_wb32(pb, 0x00480000); /* Horizontal resolution 72dpi */ + avio_wb32(pb, 0x00480000); /* Vertical resolution 72dpi */ + avio_wb32(pb, 0); /* Data size (= 0) */ + avio_wb16(pb, 1); /* Frame count (= 1) */ + + /* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */ + if (track->mode == MODE_MOV && track->enc->codec && track->enc->codec->name) + av_strlcpy(compressor_name,track->enc->codec->name,32); + avio_w8(pb, strlen(compressor_name)); + avio_write(pb, compressor_name, 31); + + if (track->mode == MODE_MOV && track->enc->bits_per_coded_sample) + avio_wb16(pb, track->enc->bits_per_coded_sample); + else + avio_wb16(pb, 0x18); /* Reserved */ + avio_wb16(pb, 0xffff); /* Reserved */ + if(track->tag == MKTAG('m','p','4','v')) + mov_write_esds_tag(pb, track); + else if(track->enc->codec_id == AV_CODEC_ID_H263) + mov_write_d263_tag(pb); + else if(track->enc->codec_id == AV_CODEC_ID_AVUI || + track->enc->codec_id == AV_CODEC_ID_SVQ3) { + mov_write_extradata_tag(pb, track); + avio_wb32(pb, 0); + } else if(track->enc->codec_id == AV_CODEC_ID_DNXHD) + mov_write_avid_tag(pb, track); + else if(track->enc->codec_id == AV_CODEC_ID_H264) { + mov_write_avcc_tag(pb, track); + if(track->mode == MODE_IPOD) + mov_write_uuid_tag_ipod(pb); + } else if (track->enc->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0) + mov_write_dvc1_tag(pb, track); + else if (track->vos_len > 0) + mov_write_glbl_tag(pb, track); + + if (track->enc->codec_id != AV_CODEC_ID_H264 && + track->enc->codec_id != AV_CODEC_ID_MPEG4 && + track->enc->codec_id != AV_CODEC_ID_DNXHD) + if (track->enc->field_order != AV_FIELD_UNKNOWN) + mov_write_fiel_tag(pb, track); + + if (track->enc->sample_aspect_ratio.den && track->enc->sample_aspect_ratio.num && + track->enc->sample_aspect_ratio.den != track->enc->sample_aspect_ratio.num) { + mov_write_pasp_tag(pb, track); + } + + return update_size(pb, pos); +} + +static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int frame_duration = av_rescale(track->timescale, track->enc->time_base.num, track->enc->time_base.den); + int nb_frames = 1.0/av_q2d(track->enc->time_base) + 0.5; + + if (nb_frames > 255) { + av_log(NULL, AV_LOG_ERROR, "fps %d is too large\n", nb_frames); + return AVERROR(EINVAL); + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tmcd"); /* Data format */ + avio_wb32(pb, 0); /* Reserved */ + avio_wb32(pb, 1); /* Data reference index */ + avio_wb32(pb, 0); /* Flags */ + avio_wb32(pb, track->timecode_flags); /* Flags (timecode) */ + avio_wb32(pb, track->timescale); /* Timescale */ + avio_wb32(pb, frame_duration); /* Frame duration */ + avio_w8(pb, nb_frames); /* Number of frames */ + avio_wb24(pb, 0); /* Reserved */ + /* TODO: source reference string */ + return update_size(pb, pos); +} + +static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "rtp "); + avio_wb32(pb, 0); /* Reserved */ + avio_wb16(pb, 0); /* Reserved */ + avio_wb16(pb, 1); /* Data-reference index */ + + avio_wb16(pb, 1); /* Hint track version */ + avio_wb16(pb, 1); /* Highest compatible version */ + avio_wb32(pb, track->max_packet_size); /* Max packet size */ + + avio_wb32(pb, 12); /* size */ + ffio_wfourcc(pb, "tims"); + avio_wb32(pb, track->timescale); + + return update_size(pb, pos); +} + +static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "stsd"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb32(pb, 1); /* entry count */ + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) + mov_write_video_tag(pb, track); + else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + mov_write_audio_tag(pb, track); + else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) + mov_write_subtitle_tag(pb, track); + else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) + mov_write_rtp_tag(pb, track); + else if (track->enc->codec_tag == MKTAG('t','m','c','d')) + mov_write_tmcd_tag(pb, track); + return update_size(pb, pos); +} + +static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track) +{ + MOVStts *ctts_entries; + uint32_t entries = 0; + uint32_t atom_size; + int i; + + ctts_entries = av_malloc((track->entry + 1) * sizeof(*ctts_entries)); /* worst case */ + ctts_entries[0].count = 1; + ctts_entries[0].duration = track->cluster[0].cts; + for (i=1; ientry; i++) { + if (track->cluster[i].cts == ctts_entries[entries].duration) { + ctts_entries[entries].count++; /* compress */ + } else { + entries++; + ctts_entries[entries].duration = track->cluster[i].cts; + ctts_entries[entries].count = 1; + } + } + entries++; /* last one */ + atom_size = 16 + (entries * 8); + avio_wb32(pb, atom_size); /* size */ + ffio_wfourcc(pb, "ctts"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb32(pb, entries); /* entry count */ + for (i=0; ienc->codec_type == AVMEDIA_TYPE_AUDIO && !track->audio_vbr) { + stts_entries = av_malloc(sizeof(*stts_entries)); /* one entry */ + stts_entries[0].count = track->sample_count; + stts_entries[0].duration = 1; + entries = 1; + } else { + stts_entries = track->entry ? + av_malloc(track->entry * sizeof(*stts_entries)) : /* worst case */ + NULL; + for (i=0; ientry; i++) { + int duration = get_cluster_duration(track, i); + if (i && duration == stts_entries[entries].duration) { + stts_entries[entries].count++; /* compress */ + } else { + entries++; + stts_entries[entries].duration = duration; + stts_entries[entries].count = 1; + } + } + entries++; /* last one */ + } + atom_size = 16 + (entries * 8); + avio_wb32(pb, atom_size); /* size */ + ffio_wfourcc(pb, "stts"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb32(pb, entries); /* entry count */ + for (i=0; ienc->codec_type == AVMEDIA_TYPE_VIDEO || + track->enc->codec_tag == MKTAG('r','t','p',' ')) && + track->has_keyframes && track->has_keyframes < track->entry) + mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE); + if (track->mode == MODE_MOV && track->flags & MOV_TRACK_STPS) + mov_write_stss_tag(pb, track, MOV_PARTIAL_SYNC_SAMPLE); + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO && + track->flags & MOV_TRACK_CTTS) + mov_write_ctts_tag(pb, track); + mov_write_stsc_tag(pb, track); + mov_write_stsz_tag(pb, track); + mov_write_stco_tag(pb, track); + return update_size(pb, pos); +} + +static int mov_write_dinf_tag(AVIOContext *pb) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "dinf"); + mov_write_dref_tag(pb); + return update_size(pb, pos); +} + +static int mov_write_nmhd_tag(AVIOContext *pb) +{ + avio_wb32(pb, 12); + ffio_wfourcc(pb, "nmhd"); + avio_wb32(pb, 0); + return 12; +} + +static int mov_write_tcmi_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + const char *font = "Lucida Grande"; + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tcmi"); /* timecode media information atom */ + avio_wb32(pb, 0); /* version & flags */ + avio_wb16(pb, 0); /* text font */ + avio_wb16(pb, 0); /* text face */ + avio_wb16(pb, 12); /* text size */ + avio_wb16(pb, 0); /* (unknown, not in the QT specs...) */ + avio_wb16(pb, 0x0000); /* text color (red) */ + avio_wb16(pb, 0x0000); /* text color (green) */ + avio_wb16(pb, 0x0000); /* text color (blue) */ + avio_wb16(pb, 0xffff); /* background color (red) */ + avio_wb16(pb, 0xffff); /* background color (green) */ + avio_wb16(pb, 0xffff); /* background color (blue) */ + avio_w8(pb, strlen(font)); /* font len (part of the pascal string) */ + avio_write(pb, font, strlen(font)); /* font name */ + return update_size(pb, pos); +} + +static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "gmhd"); + avio_wb32(pb, 0x18); /* gmin size */ + ffio_wfourcc(pb, "gmin");/* generic media info */ + avio_wb32(pb, 0); /* version & flags */ + avio_wb16(pb, 0x40); /* graphics mode = */ + avio_wb16(pb, 0x8000); /* opColor (r?) */ + avio_wb16(pb, 0x8000); /* opColor (g?) */ + avio_wb16(pb, 0x8000); /* opColor (b?) */ + avio_wb16(pb, 0); /* balance */ + avio_wb16(pb, 0); /* reserved */ + + /* + * This special text atom is required for + * Apple Quicktime chapters. The contents + * don't appear to be documented, so the + * bytes are copied verbatim. + */ + if (track->tag != MKTAG('c','6','0','8')) { + avio_wb32(pb, 0x2C); /* size */ + ffio_wfourcc(pb, "text"); + avio_wb16(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00004000); + avio_wb16(pb, 0x0000); + } + + if (track->enc->codec_tag == MKTAG('t','m','c','d')) { + int64_t tmcd_pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tmcd"); + mov_write_tcmi_tag(pb, track); + update_size(pb, tmcd_pos); + } + return update_size(pb, pos); +} + +static int mov_write_smhd_tag(AVIOContext *pb) +{ + avio_wb32(pb, 16); /* size */ + ffio_wfourcc(pb, "smhd"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb16(pb, 0); /* reserved (balance, normally = 0) */ + avio_wb16(pb, 0); /* reserved */ + return 16; +} + +static int mov_write_vmhd_tag(AVIOContext *pb) +{ + avio_wb32(pb, 0x14); /* size (always 0x14) */ + ffio_wfourcc(pb, "vmhd"); + avio_wb32(pb, 0x01); /* version & flags */ + avio_wb64(pb, 0); /* reserved (graphics mode = copy) */ + return 0x14; +} + +static int mov_write_hdlr_tag(AVIOContext *pb, MOVTrack *track) +{ + const char *hdlr, *descr = NULL, *hdlr_type = NULL; + int64_t pos = avio_tell(pb); + + if (!track) { /* no media --> data handler */ + hdlr = "dhlr"; + hdlr_type = "url "; + descr = "DataHandler"; + } else { + hdlr = (track->mode == MODE_MOV) ? "mhlr" : "\0\0\0\0"; + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { + hdlr_type = "vide"; + descr = "VideoHandler"; + } else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) { + hdlr_type = "soun"; + descr = "SoundHandler"; + } else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (track->tag == MKTAG('c','6','0','8')) { + hdlr_type = "clcp"; + descr = "ClosedCaptionHandler"; + } else { + if (track->tag == MKTAG('t','x','3','g')) hdlr_type = "sbtl"; + else hdlr_type = "text"; + descr = "SubtitleHandler"; + } + } else if (track->enc->codec_tag == MKTAG('t','m','c','d')) { + hdlr_type = "tmcd"; + descr = "TimeCodeHandler"; + } else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) { + hdlr_type = "hint"; + descr = "HintHandler"; + } else { + hdlr = "dhlr"; + hdlr_type = "url "; + descr = "DataHandler"; + } + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "hdlr"); + avio_wb32(pb, 0); /* Version & flags */ + avio_write(pb, hdlr, 4); /* handler */ + ffio_wfourcc(pb, hdlr_type); /* handler type */ + avio_wb32(pb ,0); /* reserved */ + avio_wb32(pb ,0); /* reserved */ + avio_wb32(pb ,0); /* reserved */ + if (!track || track->mode == MODE_MOV) + avio_w8(pb, strlen(descr)); /* pascal string */ + avio_write(pb, descr, strlen(descr)); /* handler description */ + if (track && track->mode != MODE_MOV) + avio_w8(pb, 0); /* c string */ + return update_size(pb, pos); +} + +static int mov_write_hmhd_tag(AVIOContext *pb) +{ + /* This atom must be present, but leaving the values at zero + * seems harmless. */ + avio_wb32(pb, 28); /* size */ + ffio_wfourcc(pb, "hmhd"); + avio_wb32(pb, 0); /* version, flags */ + avio_wb16(pb, 0); /* maxPDUsize */ + avio_wb16(pb, 0); /* avgPDUsize */ + avio_wb32(pb, 0); /* maxbitrate */ + avio_wb32(pb, 0); /* avgbitrate */ + avio_wb32(pb, 0); /* reserved */ + return 28; +} + +static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "minf"); + if(track->enc->codec_type == AVMEDIA_TYPE_VIDEO) + mov_write_vmhd_tag(pb); + else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + mov_write_smhd_tag(pb); + else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (track->tag == MKTAG('t','e','x','t') || track->tag == MKTAG('c','6','0','8')) { + mov_write_gmhd_tag(pb, track); + } else { + mov_write_nmhd_tag(pb); + } + } else if (track->tag == MKTAG('t','m','c','d')) { + mov_write_gmhd_tag(pb, track); + } else if (track->tag == MKTAG('r','t','p',' ')) { + mov_write_hmhd_tag(pb); + } + if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ + mov_write_hdlr_tag(pb, NULL); + mov_write_dinf_tag(pb); + mov_write_stbl_tag(pb, track); + return update_size(pb, pos); +} + +static int mov_write_mdhd_tag(AVIOContext *pb, MOVTrack *track) +{ + int version = track->track_duration < INT32_MAX ? 0 : 1; + + if (track->mode == MODE_ISM) + version = 1; + + (version == 1) ? avio_wb32(pb, 44) : avio_wb32(pb, 32); /* size */ + ffio_wfourcc(pb, "mdhd"); + avio_w8(pb, version); + avio_wb24(pb, 0); /* flags */ + if (version == 1) { + avio_wb64(pb, track->time); + avio_wb64(pb, track->time); + } else { + avio_wb32(pb, track->time); /* creation time */ + avio_wb32(pb, track->time); /* modification time */ + } + avio_wb32(pb, track->timescale); /* time scale (sample rate for audio) */ + if (!track->entry) + (version == 1) ? avio_wb64(pb, UINT64_C(0xffffffffffffffff)) : avio_wb32(pb, 0xffffffff); + else + (version == 1) ? avio_wb64(pb, track->track_duration) : avio_wb32(pb, track->track_duration); /* duration */ + avio_wb16(pb, track->language); /* language */ + avio_wb16(pb, 0); /* reserved (quality) */ + + if(version!=0 && track->mode == MODE_MOV){ + av_log(NULL, AV_LOG_ERROR, + "FATAL error, file duration too long for timebase, this file will not be\n" + "playable with quicktime. Choose a different timebase or a different\n" + "container format\n"); + } + + return 32; +} + +static int mov_write_mdia_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "mdia"); + mov_write_mdhd_tag(pb, track); + mov_write_hdlr_tag(pb, track); + mov_write_minf_tag(pb, track); + return update_size(pb, pos); +} + +/* transformation matrix + |a b u| + |c d v| + |tx ty w| */ +static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c, + int16_t d, int16_t tx, int16_t ty) +{ + avio_wb32(pb, a << 16); /* 16.16 format */ + avio_wb32(pb, b << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* u in 2.30 format */ + avio_wb32(pb, c << 16); /* 16.16 format */ + avio_wb32(pb, d << 16); /* 16.16 format */ + avio_wb32(pb, 0); /* v in 2.30 format */ + avio_wb32(pb, tx << 16); /* 16.16 format */ + avio_wb32(pb, ty << 16); /* 16.16 format */ + avio_wb32(pb, 1 << 30); /* w in 2.30 format */ +} + +static int mov_write_tkhd_tag(AVIOContext *pb, MOVTrack *track, AVStream *st) +{ + int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, + track->timescale, AV_ROUND_UP); + int version = duration < INT32_MAX ? 0 : 1; + int rotation = 0; + + if (track->mode == MODE_ISM) + version = 1; + + (version == 1) ? avio_wb32(pb, 104) : avio_wb32(pb, 92); /* size */ + ffio_wfourcc(pb, "tkhd"); + avio_w8(pb, version); + avio_wb24(pb, track->secondary ? 0x2 : 0xf); /* flags (first track enabled) */ + if (version == 1) { + avio_wb64(pb, track->time); + avio_wb64(pb, track->time); + } else { + avio_wb32(pb, track->time); /* creation time */ + avio_wb32(pb, track->time); /* modification time */ + } + avio_wb32(pb, track->track_id); /* track-id */ + avio_wb32(pb, 0); /* reserved */ + if (!track->entry) + (version == 1) ? avio_wb64(pb, UINT64_C(0xffffffffffffffff)) : avio_wb32(pb, 0xffffffff); + else + (version == 1) ? avio_wb64(pb, duration) : avio_wb32(pb, duration); + + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb16(pb, 0); /* layer */ + avio_wb16(pb, st ? st->codec->codec_type : 0); /* alternate group) */ + /* Volume, only for audio */ + if(track->enc->codec_type == AVMEDIA_TYPE_AUDIO) + avio_wb16(pb, 0x0100); + else + avio_wb16(pb, 0); + avio_wb16(pb, 0); /* reserved */ + + /* Matrix structure */ + if (st && st->metadata) { + AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0); + rotation = (rot && rot->value) ? atoi(rot->value) : 0; + } + if (rotation == 90) { + write_matrix(pb, 0, 1, -1, 0, track->enc->height, 0); + } else if (rotation == 180) { + write_matrix(pb, -1, 0, 0, -1, track->enc->width, track->enc->height); + } else if (rotation == 270) { + write_matrix(pb, 0, -1, 1, 0, 0, track->enc->width); + } else { + write_matrix(pb, 1, 0, 0, 1, 0, 0); + } + /* Track width and height, for visual only */ + if(st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO || + track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) { + if(track->mode == MODE_MOV) { + avio_wb32(pb, track->enc->width << 16); + avio_wb32(pb, track->height << 16); + } else { + double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio); + if(!sample_aspect_ratio || track->height != track->enc->height) + sample_aspect_ratio = 1; + avio_wb32(pb, sample_aspect_ratio * track->enc->width*0x10000); + avio_wb32(pb, track->height*0x10000); + } + } + else { + avio_wb32(pb, 0); + avio_wb32(pb, 0); + } + return 0x5c; +} + +static int mov_write_tapt_tag(AVIOContext *pb, MOVTrack *track) +{ + int32_t width = av_rescale(track->enc->sample_aspect_ratio.num, track->enc->width, + track->enc->sample_aspect_ratio.den); + + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "tapt"); + + avio_wb32(pb, 20); + ffio_wfourcc(pb, "clef"); + avio_wb32(pb, 0); + avio_wb32(pb, width << 16); + avio_wb32(pb, track->enc->height << 16); + + avio_wb32(pb, 20); + ffio_wfourcc(pb, "prof"); + avio_wb32(pb, 0); + avio_wb32(pb, width << 16); + avio_wb32(pb, track->enc->height << 16); + + avio_wb32(pb, 20); + ffio_wfourcc(pb, "enof"); + avio_wb32(pb, 0); + avio_wb32(pb, track->enc->width << 16); + avio_wb32(pb, track->enc->height << 16); + + return update_size(pb, pos); +} + +// This box seems important for the psp playback ... without it the movie seems to hang +static int mov_write_edts_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, + track->timescale, AV_ROUND_UP); + int version = duration < INT32_MAX ? 0 : 1; + int entry_size, entry_count, size; + int64_t delay, start_ct = track->cluster[0].cts; + delay = av_rescale_rnd(track->cluster[0].dts + start_ct, MOV_TIMESCALE, + track->timescale, AV_ROUND_DOWN); + version |= delay < INT32_MAX ? 0 : 1; + + entry_size = (version == 1) ? 20 : 12; + entry_count = 1 + (delay > 0); + size = 24 + entry_count * entry_size; + + /* write the atom data */ + avio_wb32(pb, size); + ffio_wfourcc(pb, "edts"); + avio_wb32(pb, size - 8); + ffio_wfourcc(pb, "elst"); + avio_w8(pb, version); + avio_wb24(pb, 0); /* flags */ + + avio_wb32(pb, entry_count); + if (delay > 0) { /* add an empty edit to delay presentation */ + if (version == 1) { + avio_wb64(pb, delay); + avio_wb64(pb, -1); + } else { + avio_wb32(pb, delay); + avio_wb32(pb, -1); + } + avio_wb32(pb, 0x00010000); + } else { + av_assert0(av_rescale_rnd(track->cluster[0].dts, MOV_TIMESCALE, track->timescale, AV_ROUND_DOWN) <= 0); + start_ct = -FFMIN(track->cluster[0].dts, 0); //FFMIN needed due to rounding + duration += delay; + } + + /* duration */ + if (version == 1) { + avio_wb64(pb, duration); + avio_wb64(pb, start_ct); + } else { + avio_wb32(pb, duration); + avio_wb32(pb, start_ct); + } + avio_wb32(pb, 0x00010000); + return size; +} + +static int mov_write_tref_tag(AVIOContext *pb, MOVTrack *track) +{ + avio_wb32(pb, 20); // size + ffio_wfourcc(pb, "tref"); + avio_wb32(pb, 12); // size (subatom) + avio_wl32(pb, track->tref_tag); + avio_wb32(pb, track->tref_id); + return 20; +} + +// goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it) +static int mov_write_uuid_tag_psp(AVIOContext *pb, MOVTrack *mov) +{ + avio_wb32(pb, 0x34); /* size ... reports as 28 in mp4box! */ + ffio_wfourcc(pb, "uuid"); + ffio_wfourcc(pb, "USMT"); + avio_wb32(pb, 0x21d24fce); + avio_wb32(pb, 0xbb88695c); + avio_wb32(pb, 0xfac9c740); + avio_wb32(pb, 0x1c); // another size here! + ffio_wfourcc(pb, "MTDT"); + avio_wb32(pb, 0x00010012); + avio_wb32(pb, 0x0a); + avio_wb32(pb, 0x55c40000); + avio_wb32(pb, 0x1); + avio_wb32(pb, 0x0); + return 0x34; +} + +static int mov_write_udta_sdp(AVIOContext *pb, MOVTrack *track) +{ + + AVFormatContext *ctx = track->rtp_ctx; + char buf[1000] = ""; + int len; + + ff_sdp_write_media(buf, sizeof(buf), ctx->streams[0], track->src_track, + NULL, NULL, 0, 0, ctx); + av_strlcatf(buf, sizeof(buf), "a=control:streamid=%d\r\n", track->track_id); + len = strlen(buf); + + avio_wb32(pb, len + 24); + ffio_wfourcc(pb, "udta"); + avio_wb32(pb, len + 16); + ffio_wfourcc(pb, "hnti"); + avio_wb32(pb, len + 8); + ffio_wfourcc(pb, "sdp "); + avio_write(pb, buf, len); + return len + 24; +} + +static int mov_write_trak_tag(AVIOContext *pb, MOVMuxContext *mov, + MOVTrack *track, AVStream *st) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "trak"); + mov_write_tkhd_tag(pb, track, st); + if (supports_edts(mov)) + mov_write_edts_tag(pb, track); // PSP Movies and several other cases require edts box + if (track->tref_tag) + mov_write_tref_tag(pb, track); + mov_write_mdia_tag(pb, track); + if (track->mode == MODE_PSP) + mov_write_uuid_tag_psp(pb,track); // PSP Movies require this uuid box + if (track->tag == MKTAG('r','t','p',' ')) + mov_write_udta_sdp(pb, track); + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO && track->mode == MODE_MOV) { + double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio); + if (st->sample_aspect_ratio.num && 1.0 != sample_aspect_ratio) + mov_write_tapt_tag(pb, track); + }; + return update_size(pb, pos); +} + +static int mov_write_iods_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + int i, has_audio = 0, has_video = 0; + int64_t pos = avio_tell(pb); + int audio_profile = mov->iods_audio_profile; + int video_profile = mov->iods_video_profile; + for (i = 0; i < mov->nb_streams; i++) { + if(mov->tracks[i].entry > 0) { + has_audio |= mov->tracks[i].enc->codec_type == AVMEDIA_TYPE_AUDIO; + has_video |= mov->tracks[i].enc->codec_type == AVMEDIA_TYPE_VIDEO; + } + } + if (audio_profile < 0) + audio_profile = 0xFF - has_audio; + if (video_profile < 0) + video_profile = 0xFF - has_video; + avio_wb32(pb, 0x0); /* size */ + ffio_wfourcc(pb, "iods"); + avio_wb32(pb, 0); /* version & flags */ + put_descr(pb, 0x10, 7); + avio_wb16(pb, 0x004f); + avio_w8(pb, 0xff); + avio_w8(pb, 0xff); + avio_w8(pb, audio_profile); + avio_w8(pb, video_profile); + avio_w8(pb, 0xff); + return update_size(pb, pos); +} + +static int mov_write_trex_tag(AVIOContext *pb, MOVTrack *track) +{ + avio_wb32(pb, 0x20); /* size */ + ffio_wfourcc(pb, "trex"); + avio_wb32(pb, 0); /* version & flags */ + avio_wb32(pb, track->track_id); /* track ID */ + avio_wb32(pb, 1); /* default sample description index */ + avio_wb32(pb, 0); /* default sample duration */ + avio_wb32(pb, 0); /* default sample size */ + avio_wb32(pb, 0); /* default sample flags */ + return 0; +} + +static int mov_write_mvex_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + int64_t pos = avio_tell(pb); + int i; + avio_wb32(pb, 0x0); /* size */ + ffio_wfourcc(pb, "mvex"); + for (i = 0; i < mov->nb_streams; i++) + mov_write_trex_tag(pb, &mov->tracks[i]); + return update_size(pb, pos); +} + +static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + int max_track_id = 1, i; + int64_t max_track_len_temp, max_track_len = 0; + int version; + + for (i=0; inb_streams; i++) { + if(mov->tracks[i].entry > 0) { + max_track_len_temp = av_rescale_rnd(mov->tracks[i].track_duration, + MOV_TIMESCALE, + mov->tracks[i].timescale, + AV_ROUND_UP); + if (max_track_len < max_track_len_temp) + max_track_len = max_track_len_temp; + if (max_track_id < mov->tracks[i].track_id) + max_track_id = mov->tracks[i].track_id; + } + } + + version = max_track_len < UINT32_MAX ? 0 : 1; + (version == 1) ? avio_wb32(pb, 120) : avio_wb32(pb, 108); /* size */ + ffio_wfourcc(pb, "mvhd"); + avio_w8(pb, version); + avio_wb24(pb, 0); /* flags */ + if (version == 1) { + avio_wb64(pb, mov->time); + avio_wb64(pb, mov->time); + } else { + avio_wb32(pb, mov->time); /* creation time */ + avio_wb32(pb, mov->time); /* modification time */ + } + avio_wb32(pb, MOV_TIMESCALE); + (version == 1) ? avio_wb64(pb, max_track_len) : avio_wb32(pb, max_track_len); /* duration of longest track */ + + avio_wb32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */ + avio_wb16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */ + avio_wb16(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + + /* Matrix structure */ + write_matrix(pb, 1, 0, 0, 1, 0, 0); + + avio_wb32(pb, 0); /* reserved (preview time) */ + avio_wb32(pb, 0); /* reserved (preview duration) */ + avio_wb32(pb, 0); /* reserved (poster time) */ + avio_wb32(pb, 0); /* reserved (selection time) */ + avio_wb32(pb, 0); /* reserved (selection duration) */ + avio_wb32(pb, 0); /* reserved (current time) */ + avio_wb32(pb, max_track_id + 1); /* Next track id */ + return 0x6c; +} + +static int mov_write_itunes_hdlr_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + avio_wb32(pb, 33); /* size */ + ffio_wfourcc(pb, "hdlr"); + avio_wb32(pb, 0); + avio_wb32(pb, 0); + ffio_wfourcc(pb, "mdir"); + ffio_wfourcc(pb, "appl"); + avio_wb32(pb, 0); + avio_wb32(pb, 0); + avio_w8(pb, 0); + return 33; +} + +/* helper function to write a data tag with the specified string as data */ +static int mov_write_string_data_tag(AVIOContext *pb, const char *data, int lang, int long_style) +{ + if(long_style){ + int size = 16 + strlen(data); + avio_wb32(pb, size); /* size */ + ffio_wfourcc(pb, "data"); + avio_wb32(pb, 1); + avio_wb32(pb, 0); + avio_write(pb, data, strlen(data)); + return size; + }else{ + if (!lang) + lang = ff_mov_iso639_to_lang("und", 1); + avio_wb16(pb, strlen(data)); /* string length */ + avio_wb16(pb, lang); + avio_write(pb, data, strlen(data)); + return strlen(data) + 4; + } +} + +static int mov_write_string_tag(AVIOContext *pb, const char *name, const char *value, int lang, int long_style){ + int size = 0; + if (value && value[0]) { + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, name); + mov_write_string_data_tag(pb, value, lang, long_style); + size = update_size(pb, pos); + } + return size; +} + +static int mov_write_string_metadata(AVFormatContext *s, AVIOContext *pb, + const char *name, const char *tag, + int long_style) +{ + int l, lang = 0, len, len2; + AVDictionaryEntry *t, *t2 = NULL; + char tag2[16]; + + if (!(t = av_dict_get(s->metadata, tag, NULL, 0))) + return 0; + + len = strlen(t->key); + snprintf(tag2, sizeof(tag2), "%s-", tag); + while ((t2 = av_dict_get(s->metadata, tag2, t2, AV_DICT_IGNORE_SUFFIX))) { + len2 = strlen(t2->key); + if (len2 == len+4 && !strcmp(t->value, t2->value) + && (l=ff_mov_iso639_to_lang(&t2->key[len2-3], 1)) >= 0) { + lang = l; + break; + } + } + return mov_write_string_tag(pb, name, t->value, lang, long_style); +} + +/* iTunes bpm number */ +static int mov_write_tmpo_tag(AVIOContext *pb, AVFormatContext *s) +{ + AVDictionaryEntry *t = av_dict_get(s->metadata, "tmpo", NULL, 0); + int size = 0, tmpo = t ? atoi(t->value) : 0; + if (tmpo) { + size = 26; + avio_wb32(pb, size); + ffio_wfourcc(pb, "tmpo"); + avio_wb32(pb, size-8); /* size */ + ffio_wfourcc(pb, "data"); + avio_wb32(pb, 0x15); //type specifier + avio_wb32(pb, 0); + avio_wb16(pb, tmpo); // data + } + return size; +} + +/* iTunes track number */ +static int mov_write_trkn_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + AVDictionaryEntry *t = av_dict_get(s->metadata, "track", NULL, 0); + int size = 0, track = t ? atoi(t->value) : 0; + if (track) { + avio_wb32(pb, 32); /* size */ + ffio_wfourcc(pb, "trkn"); + avio_wb32(pb, 24); /* size */ + ffio_wfourcc(pb, "data"); + avio_wb32(pb, 0); // 8 bytes empty + avio_wb32(pb, 0); + avio_wb16(pb, 0); // empty + avio_wb16(pb, track); // track number + avio_wb16(pb, 0); // total track number + avio_wb16(pb, 0); // empty + size = 32; + } + return size; +} + +static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb, + const char *name, const char *tag, + int len) +{ + AVDictionaryEntry *t = NULL; + uint8_t num; + + if (!(t = av_dict_get(s->metadata, tag, NULL, 0))) + return 0; + num = atoi(t->value); + + avio_wb32(pb, len+8); + ffio_wfourcc(pb, name); + if (len==4) avio_wb32(pb, num); + else avio_w8 (pb, num); + return len+8; +} + +/* iTunes meta data list */ +static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "ilst"); + mov_write_string_metadata(s, pb, "\251nam", "title" , 1); + mov_write_string_metadata(s, pb, "\251ART", "artist" , 1); + mov_write_string_metadata(s, pb, "aART", "album_artist", 1); + mov_write_string_metadata(s, pb, "\251wrt", "composer" , 1); + mov_write_string_metadata(s, pb, "\251alb", "album" , 1); + mov_write_string_metadata(s, pb, "\251day", "date" , 1); + mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 0, 1); + mov_write_string_metadata(s, pb, "\251cmt", "comment" , 1); + mov_write_string_metadata(s, pb, "\251gen", "genre" , 1); + mov_write_string_metadata(s, pb, "\251cpy", "copyright", 1); + mov_write_string_metadata(s, pb, "\251grp", "grouping" , 1); + mov_write_string_metadata(s, pb, "\251lyr", "lyrics" , 1); + mov_write_string_metadata(s, pb, "desc", "description",1); + mov_write_string_metadata(s, pb, "ldes", "synopsis" , 1); + mov_write_string_metadata(s, pb, "tvsh", "show" , 1); + mov_write_string_metadata(s, pb, "tven", "episode_id",1); + mov_write_string_metadata(s, pb, "tvnn", "network" , 1); + mov_write_int8_metadata (s, pb, "tves", "episode_sort",4); + mov_write_int8_metadata (s, pb, "tvsn", "season_number",4); + mov_write_int8_metadata (s, pb, "stik", "media_type",1); + mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1); + mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1); + mov_write_trkn_tag(pb, mov, s); + mov_write_tmpo_tag(pb, s); + return update_size(pb, pos); +} + +/* iTunes meta data tag */ +static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + int size = 0; + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "meta"); + avio_wb32(pb, 0); + mov_write_itunes_hdlr_tag(pb, mov, s); + mov_write_ilst_tag(pb, mov, s); + size = update_size(pb, pos); + return size; +} + +static int utf8len(const uint8_t *b) +{ + int len=0; + int val; + while(*b){ + GET_UTF8(val, *b++, return -1;) + len++; + } + return len; +} + +static int ascii_to_wc(AVIOContext *pb, const uint8_t *b) +{ + int val; + while(*b){ + GET_UTF8(val, *b++, return -1;) + avio_wb16(pb, val); + } + avio_wb16(pb, 0x00); + return 0; +} + +static uint16_t language_code(const char *str) +{ + return (((str[0]-0x60) & 0x1F) << 10) + (((str[1]-0x60) & 0x1F) << 5) + ((str[2]-0x60) & 0x1F); +} + +static int mov_write_3gp_udta_tag(AVIOContext *pb, AVFormatContext *s, + const char *tag, const char *str) +{ + int64_t pos = avio_tell(pb); + AVDictionaryEntry *t = av_dict_get(s->metadata, str, NULL, 0); + if (!t || !utf8len(t->value)) + return 0; + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, tag); /* type */ + avio_wb32(pb, 0); /* version + flags */ + if (!strcmp(tag, "yrrc")) + avio_wb16(pb, atoi(t->value)); + else { + avio_wb16(pb, language_code("eng")); /* language */ + avio_write(pb, t->value, strlen(t->value)+1); /* UTF8 string value */ + if (!strcmp(tag, "albm") && + (t = av_dict_get(s->metadata, "track", NULL, 0))) + avio_w8(pb, atoi(t->value)); + } + return update_size(pb, pos); +} + +static int mov_write_chpl_tag(AVIOContext *pb, AVFormatContext *s) +{ + int64_t pos = avio_tell(pb); + int i, nb_chapters = FFMIN(s->nb_chapters, 255); + + avio_wb32(pb, 0); // size + ffio_wfourcc(pb, "chpl"); + avio_wb32(pb, 0x01000000); // version + flags + avio_wb32(pb, 0); // unknown + avio_w8(pb, nb_chapters); + + for (i = 0; i < nb_chapters; i++) { + AVChapter *c = s->chapters[i]; + AVDictionaryEntry *t; + avio_wb64(pb, av_rescale_q(c->start, c->time_base, (AVRational){1,10000000})); + + if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { + int len = FFMIN(strlen(t->value), 255); + avio_w8(pb, len); + avio_write(pb, t->value, len); + } else + avio_w8(pb, 0); + } + return update_size(pb, pos); +} + +static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + AVIOContext *pb_buf; + int i, ret, size; + uint8_t *buf; + + for (i = 0; i < s->nb_streams; i++) + if (mov->tracks[i].enc->flags & CODEC_FLAG_BITEXACT) { + return 0; + } + + ret = avio_open_dyn_buf(&pb_buf); + if(ret < 0) + return ret; + + if (mov->mode & MODE_3GP) { + mov_write_3gp_udta_tag(pb_buf, s, "perf", "artist"); + mov_write_3gp_udta_tag(pb_buf, s, "titl", "title"); + mov_write_3gp_udta_tag(pb_buf, s, "auth", "author"); + mov_write_3gp_udta_tag(pb_buf, s, "gnre", "genre"); + mov_write_3gp_udta_tag(pb_buf, s, "dscp", "comment"); + mov_write_3gp_udta_tag(pb_buf, s, "albm", "album"); + mov_write_3gp_udta_tag(pb_buf, s, "cprt", "copyright"); + mov_write_3gp_udta_tag(pb_buf, s, "yrrc", "date"); + } else if (mov->mode == MODE_MOV) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4 + mov_write_string_metadata(s, pb_buf, "\251ART", "artist" , 0); + mov_write_string_metadata(s, pb_buf, "\251nam", "title" , 0); + mov_write_string_metadata(s, pb_buf, "\251aut", "author" , 0); + mov_write_string_metadata(s, pb_buf, "\251alb", "album" , 0); + mov_write_string_metadata(s, pb_buf, "\251day", "date" , 0); + mov_write_string_metadata(s, pb_buf, "\251swr", "encoder" , 0); + // currently ignored by mov.c + mov_write_string_metadata(s, pb_buf, "\251des", "comment" , 0); + // add support for libquicktime, this atom is also actually read by mov.c + mov_write_string_metadata(s, pb_buf, "\251cmt", "comment" , 0); + mov_write_string_metadata(s, pb_buf, "\251gen", "genre" , 0); + mov_write_string_metadata(s, pb_buf, "\251cpy", "copyright" , 0); + } else { + /* iTunes meta data */ + mov_write_meta_tag(pb_buf, mov, s); + } + + if (s->nb_chapters) + mov_write_chpl_tag(pb_buf, s); + + if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { + avio_wb32(pb, size+8); + ffio_wfourcc(pb, "udta"); + avio_write(pb, buf, size); + } + av_free(buf); + + return 0; +} + +static void mov_write_psp_udta_tag(AVIOContext *pb, + const char *str, const char *lang, int type) +{ + int len = utf8len(str)+1; + if(len<=0) + return; + avio_wb16(pb, len*2+10); /* size */ + avio_wb32(pb, type); /* type */ + avio_wb16(pb, language_code(lang)); /* language */ + avio_wb16(pb, 0x01); /* ? */ + ascii_to_wc(pb, str); +} + +static int mov_write_uuidusmt_tag(AVIOContext *pb, AVFormatContext *s) +{ + AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0); + int64_t pos, pos2; + + if (title) { + pos = avio_tell(pb); + avio_wb32(pb, 0); /* size placeholder*/ + ffio_wfourcc(pb, "uuid"); + ffio_wfourcc(pb, "USMT"); + avio_wb32(pb, 0x21d24fce); /* 96 bit UUID */ + avio_wb32(pb, 0xbb88695c); + avio_wb32(pb, 0xfac9c740); + + pos2 = avio_tell(pb); + avio_wb32(pb, 0); /* size placeholder*/ + ffio_wfourcc(pb, "MTDT"); + avio_wb16(pb, 4); + + // ? + avio_wb16(pb, 0x0C); /* size */ + avio_wb32(pb, 0x0B); /* type */ + avio_wb16(pb, language_code("und")); /* language */ + avio_wb16(pb, 0x0); /* ? */ + avio_wb16(pb, 0x021C); /* data */ + + mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT, "eng", 0x04); + mov_write_psp_udta_tag(pb, title->value, "eng", 0x01); + mov_write_psp_udta_tag(pb, "2006/04/01 11:11:11", "und", 0x03); + + update_size(pb, pos2); + return update_size(pb, pos); + } + + return 0; +} + +static void build_chunks(MOVTrack *trk) +{ + int i; + MOVIentry *chunk= &trk->cluster[0]; + uint64_t chunkSize = chunk->size; + chunk->chunkNum= 1; + if (trk->chunkCount) + return; + trk->chunkCount= 1; + for(i=1; ientry; i++){ + if(chunk->pos + chunkSize == trk->cluster[i].pos && + chunkSize + trk->cluster[i].size < (1<<20)){ + chunkSize += trk->cluster[i].size; + chunk->samples_in_chunk += trk->cluster[i].entries; + }else{ + trk->cluster[i].chunkNum = chunk->chunkNum+1; + chunk=&trk->cluster[i]; + chunkSize = chunk->size; + trk->chunkCount++; + } + } +} + +static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, + AVFormatContext *s) +{ + int i; + int64_t pos = avio_tell(pb); + int not_first[AVMEDIA_TYPE_NB]={0}; + avio_wb32(pb, 0); /* size placeholder*/ + ffio_wfourcc(pb, "moov"); + + for (i=0; inb_streams; i++) { + if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) + continue; + + mov->tracks[i].time = mov->time; + mov->tracks[i].track_id = i+1; + + if (mov->tracks[i].entry) + build_chunks(&mov->tracks[i]); + } + + if (mov->chapter_track) + for (i=0; inb_streams; i++) { + mov->tracks[i].tref_tag = MKTAG('c','h','a','p'); + mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id; + } + for (i = 0; i < mov->nb_streams; i++) { + if (mov->tracks[i].tag == MKTAG('r','t','p',' ')) { + mov->tracks[i].tref_tag = MKTAG('h','i','n','t'); + mov->tracks[i].tref_id = + mov->tracks[mov->tracks[i].src_track].track_id; + } + } + for (i = 0; i < mov->nb_streams; i++) { + if (mov->tracks[i].tag == MKTAG('t','m','c','d')) { + int src_trk = mov->tracks[i].src_track; + mov->tracks[src_trk].tref_tag = mov->tracks[i].tag; + mov->tracks[src_trk].tref_id = mov->tracks[i].track_id; + mov->tracks[i].track_duration = mov->tracks[src_trk].track_duration; + } + } + + mov_write_mvhd_tag(pb, mov); + if (mov->mode != MODE_MOV && !mov->iods_skip) + mov_write_iods_tag(pb, mov); + for (i=0; inb_streams; i++) { + if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT) { + if(i < s->nb_streams){ + int codec_type= s->streams[i]->codec->codec_type; + if(codec_type==AVMEDIA_TYPE_AUDIO || codec_type==AVMEDIA_TYPE_SUBTITLE){ + mov->tracks[i].secondary= not_first[codec_type]; + not_first[codec_type]= 1; + } + } + mov_write_trak_tag(pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL); + } + } + if (mov->flags & FF_MOV_FLAG_FRAGMENT) + mov_write_mvex_tag(pb, mov); /* QuickTime requires trak to precede this */ + + if (mov->mode == MODE_PSP) + mov_write_uuidusmt_tag(pb, s); + else + mov_write_udta_tag(pb, mov, s); + + return update_size(pb, pos); +} + +static void param_write_int(AVIOContext *pb, const char *name, int value) +{ + avio_printf(pb, "\n", name, value); +} + +static void param_write_string(AVIOContext *pb, const char *name, const char *value) +{ + avio_printf(pb, "\n", name, value); +} + +static void param_write_hex(AVIOContext *pb, const char *name, const uint8_t *value, int len) +{ + char buf[150]; + len = FFMIN(sizeof(buf)/2 - 1, len); + ff_data_to_hex(buf, value, len, 0); + buf[2*len] = '\0'; + avio_printf(pb, "\n", name, buf); +} + +static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov) +{ + int64_t pos = avio_tell(pb); + int i; + const uint8_t uuid[] = { + 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, + 0xba, 0x2f, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 + }; + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "uuid"); + avio_write(pb, uuid, sizeof(uuid)); + avio_wb32(pb, 0); + + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + avio_printf(pb, "\n", + LIBAVFORMAT_IDENT); + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + const char *type; + /* track->track_id is initialized in write_moov, and thus isn't known + * here yet */ + int track_id = i + 1; + + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { + type = "video"; + } else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO) { + type = "audio"; + } else { + continue; + } + avio_printf(pb, "<%s systemBitrate=\"%d\">\n", type, + track->enc->bit_rate); + param_write_int(pb, "systemBitrate", track->enc->bit_rate); + param_write_int(pb, "trackID", track_id); + if (track->enc->codec_type == AVMEDIA_TYPE_VIDEO) { + if (track->enc->codec_id == AV_CODEC_ID_H264) { + uint8_t *ptr; + int size = track->enc->extradata_size; + if (!ff_avc_write_annexb_extradata(track->enc->extradata, &ptr, + &size)) { + param_write_hex(pb, "CodecPrivateData", + ptr ? ptr : track->enc->extradata, + size); + av_free(ptr); + } + param_write_string(pb, "FourCC", "H264"); + } else if (track->enc->codec_id == AV_CODEC_ID_VC1) { + param_write_string(pb, "FourCC", "WVC1"); + param_write_hex(pb, "CodecPrivateData", track->enc->extradata, + track->enc->extradata_size); + } + param_write_int(pb, "MaxWidth", track->enc->width); + param_write_int(pb, "MaxHeight", track->enc->height); + param_write_int(pb, "DisplayWidth", track->enc->width); + param_write_int(pb, "DisplayHeight", track->enc->height); + } else { + if (track->enc->codec_id == AV_CODEC_ID_AAC) { + param_write_string(pb, "FourCC", "AACL"); + } else if (track->enc->codec_id == AV_CODEC_ID_WMAPRO) { + param_write_string(pb, "FourCC", "WMAP"); + } + param_write_hex(pb, "CodecPrivateData", track->enc->extradata, + track->enc->extradata_size); + param_write_int(pb, "AudioTag", ff_codec_get_tag(ff_codec_wav_tags, + track->enc->codec_id)); + param_write_int(pb, "Channels", track->enc->channels); + param_write_int(pb, "SamplingRate", track->enc->sample_rate); + param_write_int(pb, "BitsPerSample", 16); + param_write_int(pb, "PacketSize", track->enc->block_align ? + track->enc->block_align : 4); + } + avio_printf(pb, "\n", type); + } + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + avio_printf(pb, "\n"); + + return update_size(pb, pos); +} + +static int mov_write_mfhd_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + avio_wb32(pb, 16); + ffio_wfourcc(pb, "mfhd"); + avio_wb32(pb, 0); + avio_wb32(pb, mov->fragments); + return 0; +} + +static int mov_write_tfhd_tag(AVIOContext *pb, MOVTrack *track, + int64_t moof_offset) +{ + int64_t pos = avio_tell(pb); + uint32_t flags = MOV_TFHD_DEFAULT_SIZE | MOV_TFHD_DEFAULT_DURATION | + MOV_TFHD_BASE_DATA_OFFSET; + if (!track->entry) { + flags |= MOV_TFHD_DURATION_IS_EMPTY; + } else { + flags |= MOV_TFHD_DEFAULT_FLAGS; + } + + /* Don't set a default sample size, the silverlight player refuses + * to play files with that set. Don't set a default sample duration, + * WMP freaks out if it is set. */ + if (track->mode == MODE_ISM) + flags &= ~(MOV_TFHD_DEFAULT_SIZE | MOV_TFHD_DEFAULT_DURATION); + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "tfhd"); + avio_w8(pb, 0); /* version */ + avio_wb24(pb, flags); + + avio_wb32(pb, track->track_id); /* track-id */ + if (flags & MOV_TFHD_BASE_DATA_OFFSET) + avio_wb64(pb, moof_offset); + if (flags & MOV_TFHD_DEFAULT_DURATION) { + track->default_duration = get_cluster_duration(track, 0); + avio_wb32(pb, track->default_duration); + } + if (flags & MOV_TFHD_DEFAULT_SIZE) { + track->default_size = track->entry ? track->cluster[0].size : 1; + avio_wb32(pb, track->default_size); + } else + track->default_size = -1; + + if (flags & MOV_TFHD_DEFAULT_FLAGS) { + track->default_sample_flags = + track->enc->codec_type == AVMEDIA_TYPE_VIDEO ? + (MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES | MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC) : + MOV_FRAG_SAMPLE_FLAG_DEPENDS_NO; + avio_wb32(pb, track->default_sample_flags); + } + + return update_size(pb, pos); +} + +static uint32_t get_sample_flags(MOVTrack *track, MOVIentry *entry) +{ + return entry->flags & MOV_SYNC_SAMPLE ? MOV_FRAG_SAMPLE_FLAG_DEPENDS_NO : + (MOV_FRAG_SAMPLE_FLAG_DEPENDS_YES | MOV_FRAG_SAMPLE_FLAG_IS_NON_SYNC); +} + +static int mov_write_trun_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + uint32_t flags = MOV_TRUN_DATA_OFFSET; + int i; + + for (i = 0; i < track->entry; i++) { + if (get_cluster_duration(track, i) != track->default_duration) + flags |= MOV_TRUN_SAMPLE_DURATION; + if (track->cluster[i].size != track->default_size) + flags |= MOV_TRUN_SAMPLE_SIZE; + if (i > 0 && get_sample_flags(track, &track->cluster[i]) != track->default_sample_flags) + flags |= MOV_TRUN_SAMPLE_FLAGS; + } + if (!(flags & MOV_TRUN_SAMPLE_FLAGS)) + flags |= MOV_TRUN_FIRST_SAMPLE_FLAGS; + if (track->flags & MOV_TRACK_CTTS) + flags |= MOV_TRUN_SAMPLE_CTS; + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "trun"); + avio_w8(pb, 0); /* version */ + avio_wb24(pb, flags); + + avio_wb32(pb, track->entry); /* sample count */ + track->moof_size_offset = avio_tell(pb); + avio_wb32(pb, 0); /* data offset */ + if (flags & MOV_TRUN_FIRST_SAMPLE_FLAGS) + avio_wb32(pb, get_sample_flags(track, &track->cluster[0])); + + for (i = 0; i < track->entry; i++) { + if (flags & MOV_TRUN_SAMPLE_DURATION) + avio_wb32(pb, get_cluster_duration(track, i)); + if (flags & MOV_TRUN_SAMPLE_SIZE) + avio_wb32(pb, track->cluster[i].size); + if (flags & MOV_TRUN_SAMPLE_FLAGS) + avio_wb32(pb, get_sample_flags(track, &track->cluster[i])); + if (flags & MOV_TRUN_SAMPLE_CTS) + avio_wb32(pb, track->cluster[i].cts); + } + + return update_size(pb, pos); +} + +static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + const uint8_t uuid[] = { + 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, + 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2 + }; + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "uuid"); + avio_write(pb, uuid, sizeof(uuid)); + avio_w8(pb, 1); + avio_wb24(pb, 0); + avio_wb64(pb, track->frag_start); + avio_wb64(pb, track->start_dts + track->track_duration - + track->cluster[0].dts); + + return update_size(pb, pos); +} + +static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov, + MOVTrack *track, int entry) +{ + int n = track->nb_frag_info - 1 - entry, i; + int size = 8 + 16 + 4 + 1 + 16*n; + const uint8_t uuid[] = { + 0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95, + 0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f + }; + + if (entry < 0) + return 0; + + avio_seek(pb, track->frag_info[entry].tfrf_offset, SEEK_SET); + avio_wb32(pb, size); + ffio_wfourcc(pb, "uuid"); + avio_write(pb, uuid, sizeof(uuid)); + avio_w8(pb, 1); + avio_wb24(pb, 0); + avio_w8(pb, n); + for (i = 0; i < n; i++) { + int index = entry + 1 + i; + avio_wb64(pb, track->frag_info[index].time); + avio_wb64(pb, track->frag_info[index].duration); + } + if (n < mov->ism_lookahead) { + int free_size = 16*(mov->ism_lookahead - n); + avio_wb32(pb, free_size); + ffio_wfourcc(pb, "free"); + for (i = 0; i < free_size - 8; i++) + avio_w8(pb, 0); + } + + return 0; +} + +static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov, + MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int i; + for (i = 0; i < mov->ism_lookahead; i++) { + /* Update the tfrf tag for the last ism_lookahead fragments, + * nb_frag_info - 1 is the next fragment to be written. */ + mov_write_tfrf_tag(pb, mov, track, track->nb_frag_info - 2 - i); + } + avio_seek(pb, pos, SEEK_SET); + return 0; +} + +static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov, + MOVTrack *track, int64_t moof_offset) +{ + int64_t pos = avio_tell(pb); + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "traf"); + + mov_write_tfhd_tag(pb, track, moof_offset); + mov_write_trun_tag(pb, track); + if (mov->mode == MODE_ISM) { + mov_write_tfxd_tag(pb, track); + + if (mov->ism_lookahead) { + int i, size = 16 + 4 + 1 + 16*mov->ism_lookahead; + + track->tfrf_offset = avio_tell(pb); + avio_wb32(pb, 8 + size); + ffio_wfourcc(pb, "free"); + for (i = 0; i < size; i++) + avio_w8(pb, 0); + } + } + + return update_size(pb, pos); +} + +static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) +{ + int64_t pos = avio_tell(pb), end; + int i, moof_size; + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "moof"); + + mov_write_mfhd_tag(pb, mov); + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + if (tracks >= 0 && i != tracks) + continue; + if (!track->entry) + continue; + mov_write_traf_tag(pb, mov, track, pos); + } + + end = avio_tell(pb); + moof_size = end - pos; + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + if (tracks >= 0 && i != tracks) + continue; + if (!track->entry) + continue; + avio_seek(pb, mov->tracks[i].moof_size_offset, SEEK_SET); + avio_wb32(pb, moof_size + 8 + mov->tracks[i].data_offset); + } + avio_seek(pb, end, SEEK_SET); + + return update_size(pb, pos); +} + +static int mov_write_tfra_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int i; + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "tfra"); + avio_w8(pb, 1); /* version */ + avio_wb24(pb, 0); + + avio_wb32(pb, track->track_id); + avio_wb32(pb, 0); /* length of traf/trun/sample num */ + avio_wb32(pb, track->nb_frag_info); + for (i = 0; i < track->nb_frag_info; i++) { + avio_wb64(pb, track->frag_info[i].time); + avio_wb64(pb, track->frag_info[i].offset); + avio_w8(pb, 1); /* traf number */ + avio_w8(pb, 1); /* trun number */ + avio_w8(pb, 1); /* sample number */ + } + + return update_size(pb, pos); +} + +static int mov_write_mfra_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + int64_t pos = avio_tell(pb); + int i; + + avio_wb32(pb, 0); /* size placeholder */ + ffio_wfourcc(pb, "mfra"); + /* An empty mfra atom is enough to indicate to the publishing point that + * the stream has ended. */ + if (mov->flags & FF_MOV_FLAG_ISML) + return update_size(pb, pos); + + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + if (track->nb_frag_info) + mov_write_tfra_tag(pb, track); + } + + avio_wb32(pb, 16); + ffio_wfourcc(pb, "mfro"); + avio_wb32(pb, 0); /* version + flags */ + avio_wb32(pb, avio_tell(pb) + 4 - pos); + + return update_size(pb, pos); +} + +static int mov_write_mdat_tag(AVIOContext *pb, MOVMuxContext *mov) +{ + avio_wb32(pb, 8); // placeholder for extended size field (64 bit) + ffio_wfourcc(pb, mov->mode == MODE_MOV ? "wide" : "free"); + + mov->mdat_pos = avio_tell(pb); + avio_wb32(pb, 0); /* size placeholder*/ + ffio_wfourcc(pb, "mdat"); + return 0; +} + +/* TODO: This needs to be more general */ +static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + int64_t pos = avio_tell(pb); + int has_h264 = 0, has_video = 0; + int minor = 0x200; + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + has_video = 1; + if (st->codec->codec_id == AV_CODEC_ID_H264) + has_h264 = 1; + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "ftyp"); + + if (mov->mode == MODE_3GP) { + ffio_wfourcc(pb, has_h264 ? "3gp6" : "3gp4"); + minor = has_h264 ? 0x100 : 0x200; + } else if (mov->mode & MODE_3G2) { + ffio_wfourcc(pb, has_h264 ? "3g2b" : "3g2a"); + minor = has_h264 ? 0x20000 : 0x10000; + }else if (mov->mode == MODE_PSP) + ffio_wfourcc(pb, "MSNV"); + else if (mov->mode == MODE_MP4) + ffio_wfourcc(pb, "isom"); + else if (mov->mode == MODE_IPOD) + ffio_wfourcc(pb, has_video ? "M4V ":"M4A "); + else if (mov->mode == MODE_ISM) + ffio_wfourcc(pb, "isml"); + else if (mov->mode == MODE_F4V) + ffio_wfourcc(pb, "f4v "); + else + ffio_wfourcc(pb, "qt "); + + avio_wb32(pb, minor); + + if(mov->mode == MODE_MOV) + ffio_wfourcc(pb, "qt "); + else if (mov->mode == MODE_ISM) { + ffio_wfourcc(pb, "piff"); + ffio_wfourcc(pb, "iso2"); + } else { + ffio_wfourcc(pb, "isom"); + ffio_wfourcc(pb, "iso2"); + if(has_h264) + ffio_wfourcc(pb, "avc1"); + } + + if (mov->mode == MODE_3GP) + ffio_wfourcc(pb, has_h264 ? "3gp6":"3gp4"); + else if (mov->mode & MODE_3G2) + ffio_wfourcc(pb, has_h264 ? "3g2b":"3g2a"); + else if (mov->mode == MODE_PSP) + ffio_wfourcc(pb, "MSNV"); + else if (mov->mode == MODE_MP4) + ffio_wfourcc(pb, "mp41"); + return update_size(pb, pos); +} + +static void mov_write_uuidprof_tag(AVIOContext *pb, AVFormatContext *s) +{ + AVCodecContext *video_codec = s->streams[0]->codec; + AVCodecContext *audio_codec = s->streams[1]->codec; + int audio_rate = audio_codec->sample_rate; + int frame_rate = ((video_codec->time_base.den) * (0x10000))/ (video_codec->time_base.num); + int audio_kbitrate = audio_codec->bit_rate / 1000; + int video_kbitrate = FFMIN(video_codec->bit_rate / 1000, 800 - audio_kbitrate); + + avio_wb32(pb, 0x94); /* size */ + ffio_wfourcc(pb, "uuid"); + ffio_wfourcc(pb, "PROF"); + + avio_wb32(pb, 0x21d24fce); /* 96 bit UUID */ + avio_wb32(pb, 0xbb88695c); + avio_wb32(pb, 0xfac9c740); + + avio_wb32(pb, 0x0); /* ? */ + avio_wb32(pb, 0x3); /* 3 sections ? */ + + avio_wb32(pb, 0x14); /* size */ + ffio_wfourcc(pb, "FPRF"); + avio_wb32(pb, 0x0); /* ? */ + avio_wb32(pb, 0x0); /* ? */ + avio_wb32(pb, 0x0); /* ? */ + + avio_wb32(pb, 0x2c); /* size */ + ffio_wfourcc(pb, "APRF");/* audio */ + avio_wb32(pb, 0x0); + avio_wb32(pb, 0x2); /* TrackID */ + ffio_wfourcc(pb, "mp4a"); + avio_wb32(pb, 0x20f); + avio_wb32(pb, 0x0); + avio_wb32(pb, audio_kbitrate); + avio_wb32(pb, audio_kbitrate); + avio_wb32(pb, audio_rate); + avio_wb32(pb, audio_codec->channels); + + avio_wb32(pb, 0x34); /* size */ + ffio_wfourcc(pb, "VPRF"); /* video */ + avio_wb32(pb, 0x0); + avio_wb32(pb, 0x1); /* TrackID */ + if (video_codec->codec_id == AV_CODEC_ID_H264) { + ffio_wfourcc(pb, "avc1"); + avio_wb16(pb, 0x014D); + avio_wb16(pb, 0x0015); + } else { + ffio_wfourcc(pb, "mp4v"); + avio_wb16(pb, 0x0000); + avio_wb16(pb, 0x0103); + } + avio_wb32(pb, 0x0); + avio_wb32(pb, video_kbitrate); + avio_wb32(pb, video_kbitrate); + avio_wb32(pb, frame_rate); + avio_wb32(pb, frame_rate); + avio_wb16(pb, video_codec->width); + avio_wb16(pb, video_codec->height); + avio_wb32(pb, 0x010001); /* ? */ +} + +static int mov_parse_mpeg2_frame(AVPacket *pkt, uint32_t *flags) +{ + uint32_t c = -1; + int i, closed_gop = 0; + + for (i = 0; i < pkt->size - 4; i++) { + c = (c<<8) + pkt->data[i]; + if (c == 0x1b8) { // gop + closed_gop = pkt->data[i+4]>>6 & 0x01; + } else if (c == 0x100) { // pic + int temp_ref = (pkt->data[i+1]<<2) | (pkt->data[i+2]>>6); + if (!temp_ref || closed_gop) // I picture is not reordered + *flags = MOV_SYNC_SAMPLE; + else + *flags = MOV_PARTIAL_SYNC_SAMPLE; + break; + } + } + return 0; +} + +static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk, int fragment) +{ + const uint8_t *start, *next, *end = pkt->data + pkt->size; + int seq = 0, entry = 0; + int key = pkt->flags & AV_PKT_FLAG_KEY; + start = find_next_marker(pkt->data, end); + for (next = start; next < end; start = next) { + next = find_next_marker(start + 4, end); + switch (AV_RB32(start)) { + case VC1_CODE_SEQHDR: + seq = 1; + break; + case VC1_CODE_ENTRYPOINT: + entry = 1; + break; + case VC1_CODE_SLICE: + trk->vc1_info.slices = 1; + break; + } + } + if (!trk->entry && !fragment) { + /* First packet in first fragment */ + trk->vc1_info.first_packet_seq = seq; + trk->vc1_info.first_packet_entry = entry; + } else if ((seq && !trk->vc1_info.packet_seq) || + (entry && !trk->vc1_info.packet_entry)) { + int i; + for (i = 0; i < trk->entry; i++) + trk->cluster[i].flags &= ~MOV_SYNC_SAMPLE; + trk->has_keyframes = 0; + if (seq) + trk->vc1_info.packet_seq = 1; + if (entry) + trk->vc1_info.packet_entry = 1; + if (!fragment) { + /* First fragment */ + if ((!seq || trk->vc1_info.first_packet_seq) && + (!entry || trk->vc1_info.first_packet_entry)) { + /* First packet had the same headers as this one, readd the + * sync sample flag. */ + trk->cluster[0].flags |= MOV_SYNC_SAMPLE; + trk->has_keyframes = 1; + } + } + } + if (trk->vc1_info.packet_seq && trk->vc1_info.packet_entry) + key = seq && entry; + else if (trk->vc1_info.packet_seq) + key = seq; + else if (trk->vc1_info.packet_entry) + key = entry; + if (key) { + trk->cluster[trk->entry].flags |= MOV_SYNC_SAMPLE; + trk->has_keyframes++; + } +} + +static int get_moov_size(AVFormatContext *s) +{ + int ret; + uint8_t *buf; + AVIOContext *moov_buf; + MOVMuxContext *mov = s->priv_data; + + if ((ret = avio_open_dyn_buf(&moov_buf)) < 0) + return ret; + mov_write_moov_tag(moov_buf, mov, s); + ret = avio_close_dyn_buf(moov_buf, &buf); + av_free(buf); + return ret; +} + +static int mov_flush_fragment(AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + int i, first_track = -1; + int64_t mdat_size = 0; + + if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) + return 0; + + if (!(mov->flags & FF_MOV_FLAG_EMPTY_MOOV) && mov->fragments == 0) { + int64_t pos = avio_tell(s->pb); + uint8_t *buf; + int buf_size, moov_size; + + for (i = 0; i < mov->nb_streams; i++) + if (!mov->tracks[i].entry) + break; + /* Don't write the initial moov unless all tracks have data */ + if (i < mov->nb_streams) + return 0; + + moov_size = get_moov_size(s); + for (i = 0; i < mov->nb_streams; i++) + mov->tracks[i].data_offset = pos + moov_size + 8; + + mov_write_moov_tag(s->pb, mov, s); + + buf_size = avio_close_dyn_buf(mov->mdat_buf, &buf); + mov->mdat_buf = NULL; + avio_wb32(s->pb, buf_size + 8); + ffio_wfourcc(s->pb, "mdat"); + avio_write(s->pb, buf, buf_size); + av_free(buf); + + mov->fragments++; + mov->mdat_size = 0; + for (i = 0; i < mov->nb_streams; i++) { + if (mov->tracks[i].entry) + mov->tracks[i].frag_start += mov->tracks[i].start_dts + + mov->tracks[i].track_duration - + mov->tracks[i].cluster[0].dts; + mov->tracks[i].entry = 0; + } + avio_flush(s->pb); + return 0; + } + + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + if (mov->flags & FF_MOV_FLAG_SEPARATE_MOOF) + track->data_offset = 0; + else + track->data_offset = mdat_size; + if (!track->mdat_buf) + continue; + mdat_size += avio_tell(track->mdat_buf); + if (first_track < 0) + first_track = i; + } + + if (!mdat_size) + return 0; + + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + int buf_size, write_moof = 1, moof_tracks = -1; + uint8_t *buf; + int64_t duration = 0; + + if (track->entry) + duration = track->start_dts + track->track_duration - + track->cluster[0].dts; + if (mov->flags & FF_MOV_FLAG_SEPARATE_MOOF) { + if (!track->mdat_buf) + continue; + mdat_size = avio_tell(track->mdat_buf); + moof_tracks = i; + } else { + write_moof = i == first_track; + } + + if (write_moof) { + MOVFragmentInfo *info; + avio_flush(s->pb); + track->nb_frag_info++; + track->frag_info = av_realloc(track->frag_info, + sizeof(*track->frag_info) * + track->nb_frag_info); + info = &track->frag_info[track->nb_frag_info - 1]; + info->offset = avio_tell(s->pb); + info->time = mov->tracks[i].frag_start; + info->duration = duration; + mov_write_tfrf_tags(s->pb, mov, track); + + mov_write_moof_tag(s->pb, mov, moof_tracks); + info->tfrf_offset = track->tfrf_offset; + mov->fragments++; + + avio_wb32(s->pb, mdat_size + 8); + ffio_wfourcc(s->pb, "mdat"); + } + + if (track->entry) + track->frag_start += duration; + track->entry = 0; + if (!track->mdat_buf) + continue; + buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); + track->mdat_buf = NULL; + + avio_write(s->pb, buf, buf_size); + av_free(buf); + } + + mov->mdat_size = 0; + + avio_flush(s->pb); + return 0; +} + +int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + MOVMuxContext *mov = s->priv_data; + AVIOContext *pb = s->pb; + MOVTrack *trk = &mov->tracks[pkt->stream_index]; + AVCodecContext *enc = trk->enc; + unsigned int samples_in_chunk = 0; + int size= pkt->size; + uint8_t *reformatted_data = NULL; + + if (mov->flags & FF_MOV_FLAG_FRAGMENT) { + int ret; + if (mov->fragments > 0) { + if (!trk->mdat_buf) { + if ((ret = avio_open_dyn_buf(&trk->mdat_buf)) < 0) + return ret; + } + pb = trk->mdat_buf; + } else { + if (!mov->mdat_buf) { + if ((ret = avio_open_dyn_buf(&mov->mdat_buf)) < 0) + return ret; + } + pb = mov->mdat_buf; + } + } + + if (enc->codec_id == AV_CODEC_ID_AMR_NB) { + /* We must find out how many AMR blocks there are in one packet */ + static uint16_t packed_size[16] = + {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 1}; + int len = 0; + + while (len < size && samples_in_chunk < 100) { + len += packed_size[(pkt->data[len] >> 3) & 0x0F]; + samples_in_chunk++; + } + if (samples_in_chunk > 1) { + av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, implement a AVParser for it\n"); + return -1; + } + } else if (enc->codec_id == AV_CODEC_ID_ADPCM_MS || + enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) { + samples_in_chunk = enc->frame_size; + } else if (trk->sample_size) + samples_in_chunk = size / trk->sample_size; + else + samples_in_chunk = 1; + + /* copy extradata if it exists */ + if (trk->vos_len == 0 && enc->extradata_size > 0) { + trk->vos_len = enc->extradata_size; + trk->vos_data = av_malloc(trk->vos_len); + memcpy(trk->vos_data, enc->extradata, trk->vos_len); + } + + if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && + (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + if (!s->streams[pkt->stream_index]->nb_frames) { + av_log(s, AV_LOG_ERROR, "malformated aac bitstream, use -absf aac_adtstoasc\n"); + return -1; + } + av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); + } + if (enc->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1) { + /* from x264 or from bytestream h264 */ + /* nal reformating needed */ + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { + ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, + &size); + avio_write(pb, reformatted_data, size); + } else { + size = ff_avc_parse_nal_units(pb, pkt->data, pkt->size); + } + } else { + avio_write(pb, pkt->data, size); + } + + if ((enc->codec_id == AV_CODEC_ID_DNXHD || + enc->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len) { + /* copy frame to create needed atoms */ + trk->vos_len = size; + trk->vos_data = av_malloc(size); + if (!trk->vos_data) + return AVERROR(ENOMEM); + memcpy(trk->vos_data, pkt->data, size); + } + + if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) { + trk->cluster = av_realloc_f(trk->cluster, sizeof(*trk->cluster), (trk->entry + MOV_INDEX_CLUSTER_SIZE)); + if (!trk->cluster) + return -1; + } + + trk->cluster[trk->entry].pos = avio_tell(pb) - size; + trk->cluster[trk->entry].samples_in_chunk = samples_in_chunk; + trk->cluster[trk->entry].chunkNum = 0; + trk->cluster[trk->entry].size = size; + trk->cluster[trk->entry].entries = samples_in_chunk; + trk->cluster[trk->entry].dts = pkt->dts; + if (!trk->entry && trk->start_dts != AV_NOPTS_VALUE) { + /* First packet of a new fragment. We already wrote the duration + * of the last packet of the previous fragment based on track_duration, + * which might not exactly match our dts. Therefore adjust the dts + * of this packet to be what the previous packets duration implies. */ + trk->cluster[trk->entry].dts = trk->start_dts + trk->track_duration; + } + if (!trk->entry && trk->start_dts == AV_NOPTS_VALUE && !supports_edts(mov)) { + trk->cluster[trk->entry].dts = trk->start_dts = 0; + } + if (trk->start_dts == AV_NOPTS_VALUE) + trk->start_dts = pkt->dts; + trk->track_duration = pkt->dts - trk->start_dts + pkt->duration; + trk->last_sample_is_subtitle_end = 0; + + if (pkt->pts == AV_NOPTS_VALUE) { + av_log(s, AV_LOG_WARNING, "pts has no value\n"); + pkt->pts = pkt->dts; + } + if (pkt->dts != pkt->pts) + trk->flags |= MOV_TRACK_CTTS; + trk->cluster[trk->entry].cts = pkt->pts - pkt->dts; + trk->cluster[trk->entry].flags = 0; + if (enc->codec_id == AV_CODEC_ID_VC1) { + mov_parse_vc1_frame(pkt, trk, mov->fragments); + } else if (pkt->flags & AV_PKT_FLAG_KEY) { + if (mov->mode == MODE_MOV && enc->codec_id == AV_CODEC_ID_MPEG2VIDEO && + trk->entry > 0) { // force sync sample for the first key frame + mov_parse_mpeg2_frame(pkt, &trk->cluster[trk->entry].flags); + if (trk->cluster[trk->entry].flags & MOV_PARTIAL_SYNC_SAMPLE) + trk->flags |= MOV_TRACK_STPS; + } else { + trk->cluster[trk->entry].flags = MOV_SYNC_SAMPLE; + } + if (trk->cluster[trk->entry].flags & MOV_SYNC_SAMPLE) + trk->has_keyframes++; + } + trk->entry++; + trk->sample_count += samples_in_chunk; + mov->mdat_size += size; + + avio_flush(pb); + + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) + ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry, + reformatted_data, size); + av_free(reformatted_data); + return 0; +} + +static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) +{ + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk = &mov->tracks[pkt->stream_index]; + AVCodecContext *enc = trk->enc; + int64_t frag_duration = 0; + int size = pkt->size; + + if (!pkt->size) return 0; /* Discard 0 sized packets */ + + if (trk->entry && pkt->stream_index < s->nb_streams) + frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, + s->streams[pkt->stream_index]->time_base, + AV_TIME_BASE_Q); + if ((mov->max_fragment_duration && + frag_duration >= mov->max_fragment_duration) || + (mov->max_fragment_size && mov->mdat_size + size >= mov->max_fragment_size) || + (mov->flags & FF_MOV_FLAG_FRAG_KEYFRAME && + enc->codec_type == AVMEDIA_TYPE_VIDEO && + trk->entry && pkt->flags & AV_PKT_FLAG_KEY)) { + if (frag_duration >= mov->min_fragment_duration) + mov_flush_fragment(s); + } + + return ff_mov_write_packet(s, pkt); +} + +static int mov_write_subtitle_end_packet(AVFormatContext *s, + int stream_index, + int64_t dts) { + AVPacket end; + uint8_t data[2] = {0}; + int ret; + + av_init_packet(&end); + end.size = sizeof(data); + end.data = data; + end.pts = dts; + end.dts = dts; + end.duration = 0; + end.stream_index = stream_index; + + ret = mov_write_single_packet(s, &end); + av_free_packet(&end); + + return ret; +} + +static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + if (!pkt) { + mov_flush_fragment(s); + return 1; + } else { + int i; + MOVMuxContext *mov = s->priv_data; + + if (!pkt->size) return 0; /* Discard 0 sized packets */ + + /* + * Subtitles require special handling. + * + * 1) For full complaince, every track must have a sample at + * dts == 0, which is rarely true for subtitles. So, as soon + * as we see any packet with dts > 0, write an empty subtitle + * at dts == 0 for any subtitle track with no samples in it. + * + * 2) For each subtitle track, check if the current packet's + * dts is past the duration of the last subtitle sample. If + * so, we now need to write an end sample for that subtitle. + * + * This must be done conditionally to allow for subtitles that + * immediately replace each other, in which case an end sample + * is not needed, and is, in fact, actively harmful. + * + * 3) See mov_write_trailer for how the final end sample is + * handled. + */ + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + int ret; + + if (trk->enc->codec_id == AV_CODEC_ID_MOV_TEXT && + trk->track_duration < pkt->dts && + (trk->entry == 0 || !trk->last_sample_is_subtitle_end)) { + ret = mov_write_subtitle_end_packet(s, i, trk->track_duration); + if (ret < 0) return ret; + trk->last_sample_is_subtitle_end = 1; + } + } + + return mov_write_single_packet(s, pkt); + } +} + +// QuickTime chapters involve an additional text track with the chapter names +// as samples, and a tref pointing from the other tracks to the chapter one. +static void mov_create_chapter_track(AVFormatContext *s, int tracknum) +{ + AVIOContext *pb; + + MOVMuxContext *mov = s->priv_data; + MOVTrack *track = &mov->tracks[tracknum]; + AVPacket pkt = { .stream_index = tracknum, .flags = AV_PKT_FLAG_KEY }; + int i, len; + + track->mode = mov->mode; + track->tag = MKTAG('t','e','x','t'); + track->timescale = MOV_TIMESCALE; + track->enc = avcodec_alloc_context3(NULL); + track->enc->codec_type = AVMEDIA_TYPE_SUBTITLE; + + if (avio_open_dyn_buf(&pb) >= 0) { + int size; + uint8_t *buf; + + /* Stub header (usually for Quicktime chapter track) */ + // TextSampleEntry + avio_wb32(pb, 0x01); // displayFlags + avio_w8(pb, 0x00); // horizontal justification + avio_w8(pb, 0x00); // vertical justification + avio_w8(pb, 0x00); // bgColourRed + avio_w8(pb, 0x00); // bgColourGreen + avio_w8(pb, 0x00); // bgColourBlue + avio_w8(pb, 0x00); // bgColourAlpha + // BoxRecord + avio_wb16(pb, 0x00); // defTextBoxTop + avio_wb16(pb, 0x00); // defTextBoxLeft + avio_wb16(pb, 0x00); // defTextBoxBottom + avio_wb16(pb, 0x00); // defTextBoxRight + // StyleRecord + avio_wb16(pb, 0x00); // startChar + avio_wb16(pb, 0x00); // endChar + avio_wb16(pb, 0x01); // fontID + avio_w8(pb, 0x00); // fontStyleFlags + avio_w8(pb, 0x00); // fontSize + avio_w8(pb, 0x00); // fgColourRed + avio_w8(pb, 0x00); // fgColourGreen + avio_w8(pb, 0x00); // fgColourBlue + avio_w8(pb, 0x00); // fgColourAlpha + // FontTableBox + avio_wb32(pb, 0x0D); // box size + ffio_wfourcc(pb, "ftab"); // box atom name + avio_wb16(pb, 0x01); // entry count + // FontRecord + avio_wb16(pb, 0x01); // font ID + avio_w8(pb, 0x00); // font name length + + if ((size = avio_close_dyn_buf(pb, &buf)) > 0) { + track->enc->extradata = buf; + track->enc->extradata_size = size; + } else { + av_free(&buf); + } + } + + for (i = 0; i < s->nb_chapters; i++) { + AVChapter *c = s->chapters[i]; + AVDictionaryEntry *t; + + int64_t end = av_rescale_q(c->end, c->time_base, (AVRational){1,MOV_TIMESCALE}); + pkt.pts = pkt.dts = av_rescale_q(c->start, c->time_base, (AVRational){1,MOV_TIMESCALE}); + pkt.duration = end - pkt.dts; + + if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { + len = strlen(t->value); + pkt.size = len+2; + pkt.data = av_malloc(pkt.size); + AV_WB16(pkt.data, len); + memcpy(pkt.data+2, t->value, len); + ff_mov_write_packet(s, &pkt); + av_freep(&pkt.data); + } + } +} + +static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, const char *tcstr) +{ + int ret; + MOVMuxContext *mov = s->priv_data; + MOVTrack *track = &mov->tracks[index]; + AVStream *src_st = s->streams[src_index]; + AVTimecode tc; + AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4}; + AVRational rate = {src_st->codec->time_base.den, src_st->codec->time_base.num}; + + /* if the codec time base makes no sense, try to fallback on stream frame rate */ + if (av_timecode_check_frame_rate(rate) < 0) { + av_log(s, AV_LOG_DEBUG, "timecode: tbc=%d/%d invalid, fallback on %d/%d\n", + rate.num, rate.den, src_st->avg_frame_rate.num, src_st->avg_frame_rate.den); + rate = src_st->avg_frame_rate; + } + + /* compute the frame number */ + ret = av_timecode_init_from_string(&tc, rate, tcstr, s); + if (ret < 0) + return ret; + + /* tmcd track based on video stream */ + track->mode = mov->mode; + track->tag = MKTAG('t','m','c','d'); + track->src_track = src_index; + track->timescale = mov->tracks[src_index].timescale; + if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME) + track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME; + + /* encode context: tmcd data stream */ + track->enc = avcodec_alloc_context3(NULL); + track->enc->codec_type = AVMEDIA_TYPE_DATA; + track->enc->codec_tag = track->tag; + track->enc->time_base = src_st->codec->time_base; + + /* the tmcd track just contains one packet with the frame number */ + pkt.data = av_malloc(pkt.size); + AV_WB32(pkt.data, tc.start); + ret = ff_mov_write_packet(s, &pkt); + av_free(pkt.data); + return ret; +} + +static int mov_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + MOVMuxContext *mov = s->priv_data; + AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + int i, hint_track = 0, tmcd_track = 0; + + /* Set the FRAGMENT flag if any of the fragmentation methods are + * enabled. */ + if (mov->max_fragment_duration || mov->max_fragment_size || + mov->flags & (FF_MOV_FLAG_EMPTY_MOOV | + FF_MOV_FLAG_FRAG_KEYFRAME | + FF_MOV_FLAG_FRAG_CUSTOM)) + mov->flags |= FF_MOV_FLAG_FRAGMENT; + + /* faststart: moov at the beginning of the file, if supported */ + if (mov->flags & FF_MOV_FLAG_FASTSTART) { + if (mov->flags & FF_MOV_FLAG_FRAGMENT) + mov->flags &= ~FF_MOV_FLAG_FASTSTART; + else + mov->reserved_moov_size = -1; + } + + if (!supports_edts(mov) && s->avoid_negative_ts < 0) { + s->avoid_negative_ts = 1; + } + + /* Non-seekable output is ok if using fragmentation. If ism_lookahead + * is enabled, we don't support non-seekable output at all. */ + if (!s->pb->seekable && + ((!(mov->flags & FF_MOV_FLAG_FRAGMENT) && + !(s->oformat && !strcmp(s->oformat->name, "ismv"))) + || mov->ism_lookahead)) { + av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n"); + return -1; + } + + /* Default mode == MP4 */ + mov->mode = MODE_MP4; + + if (s->oformat != NULL) { + if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP; + else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3GP|MODE_3G2; + else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV; + else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP; + else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD; + else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM; + else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V; + + mov_write_ftyp_tag(pb,s); + if (mov->mode == MODE_PSP) { + if (s->nb_streams != 2) { + av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n"); + return -1; + } + mov_write_uuidprof_tag(pb,s); + } + } + + mov->nb_streams = s->nb_streams; + if (mov->mode & (MODE_MOV|MODE_IPOD) && s->nb_chapters) + mov->chapter_track = mov->nb_streams++; + + if (mov->flags & FF_MOV_FLAG_RTP_HINT) { + /* Add hint tracks for each audio and video stream */ + hint_track = mov->nb_streams; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || + st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mov->nb_streams++; + } + } + } + + if (mov->mode == MODE_MOV) { + tmcd_track = mov->nb_streams; + + /* +1 tmcd track for each video stream with a timecode */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + (global_tcr || av_dict_get(st->metadata, "timecode", NULL, 0))) + mov->nb_meta_tmcd++; + } + + /* check if there is already a tmcd track to remux */ + if (mov->nb_meta_tmcd) { + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_tag == MKTAG('t','m','c','d')) { + av_log(s, AV_LOG_WARNING, "You requested a copy of the original timecode track " + "so timecode metadata are now ignored\n"); + mov->nb_meta_tmcd = 0; + } + } + } + + mov->nb_streams += mov->nb_meta_tmcd; + } + + mov->tracks = av_mallocz(mov->nb_streams*sizeof(*mov->tracks)); + if (!mov->tracks) + return AVERROR(ENOMEM); + + for(i=0; inb_streams; i++){ + AVStream *st= s->streams[i]; + MOVTrack *track= &mov->tracks[i]; + AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0); + + track->enc = st->codec; + track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV); + if (track->language < 0) + track->language = 0; + track->mode = mov->mode; + track->tag = mov_find_codec_tag(s, track); + if (!track->tag) { + av_log(s, AV_LOG_ERROR, "track %d: could not find tag, " + "codec not currently supported in container\n", i); + goto error; + } + /* If hinting of this track is enabled by a later hint track, + * this is updated. */ + track->hint_track = -1; + track->start_dts = AV_NOPTS_VALUE; + if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO){ + if (track->tag == MKTAG('m','x','3','p') || track->tag == MKTAG('m','x','3','n') || + track->tag == MKTAG('m','x','4','p') || track->tag == MKTAG('m','x','4','n') || + track->tag == MKTAG('m','x','5','p') || track->tag == MKTAG('m','x','5','n')) { + if (st->codec->width != 720 || (st->codec->height != 608 && st->codec->height != 512)) { + av_log(s, AV_LOG_ERROR, "D-10/IMX must use 720x608 or 720x512 video resolution\n"); + goto error; + } + track->height = track->tag>>24 == 'n' ? 486 : 576; + } + track->timescale = st->codec->time_base.den; + while(track->timescale < 10000) + track->timescale *= 2; + if (track->mode == MODE_MOV && track->timescale > 100000) + av_log(s, AV_LOG_WARNING, + "WARNING codec timebase is very high. If duration is too long,\n" + "file may not be playable by quicktime. Specify a shorter timebase\n" + "or choose different container.\n"); + }else if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO){ + track->timescale = st->codec->sample_rate; + if(!st->codec->frame_size && !av_get_bits_per_sample(st->codec->codec_id)) { + av_log(s, AV_LOG_WARNING, "track %d: codec frame size is not set\n", i); + track->audio_vbr = 1; + }else if(st->codec->codec_id == AV_CODEC_ID_ADPCM_MS || + st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV || + st->codec->codec_id == AV_CODEC_ID_ILBC){ + if (!st->codec->block_align) { + av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i); + goto error; + } + track->sample_size = st->codec->block_align; + }else if(st->codec->frame_size > 1){ /* assume compressed audio */ + track->audio_vbr = 1; + }else{ + track->sample_size = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels; + } + if (track->mode != MODE_MOV && + track->enc->codec_id == AV_CODEC_ID_MP3 && track->timescale < 16000) { + av_log(s, AV_LOG_ERROR, "track %d: muxing mp3 at %dhz is not supported\n", + i, track->enc->sample_rate); + goto error; + } + }else if(st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE){ + track->timescale = st->codec->time_base.den; + }else{ + track->timescale = MOV_TIMESCALE; + } + if (!track->height) + track->height = st->codec->height; + /* The ism specific timescale isn't mandatory, but is assumed by + * some tools, such as mp4split. */ + if (mov->mode == MODE_ISM) + track->timescale = 10000000; + + avpriv_set_pts_info(st, 64, 1, track->timescale); + + /* copy extradata if it exists */ + if (st->codec->extradata_size) { + track->vos_len = st->codec->extradata_size; + track->vos_data = av_malloc(track->vos_len); + memcpy(track->vos_data, st->codec->extradata, track->vos_len); + } + } + + if (mov->mode == MODE_ISM) { + /* If no fragmentation options have been set, set a default. */ + if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME | + FF_MOV_FLAG_FRAG_CUSTOM)) && + !mov->max_fragment_duration && !mov->max_fragment_size) + mov->max_fragment_duration = 5000000; + mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF | + FF_MOV_FLAG_FRAGMENT; + } + + if(mov->reserved_moov_size){ + mov->reserved_moov_pos= avio_tell(pb); + if (mov->reserved_moov_size > 0) + avio_skip(pb, mov->reserved_moov_size); + } + + if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) + mov_write_mdat_tag(pb, mov); + + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) + mov->time = ff_iso8601_to_unix_time(t->value); + if (mov->time) + mov->time += 0x7C25B080; // 1970 based -> 1904 based + + if (mov->chapter_track) + mov_create_chapter_track(s, mov->chapter_track); + + if (mov->flags & FF_MOV_FLAG_RTP_HINT) { + /* Initialize the hint tracks for each audio and video stream */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || + st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + ff_mov_init_hinting(s, hint_track, i); + hint_track++; + } + } + } + + if (mov->nb_meta_tmcd) { + /* Initialize the tmcd tracks */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + t = global_tcr; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!t) + t = av_dict_get(st->metadata, "timecode", NULL, 0); + if (!t) + continue; + if (mov_create_timecode_track(s, tmcd_track, i, t->value) < 0) + goto error; + tmcd_track++; + } + } + } + + avio_flush(pb); + + if (mov->flags & FF_MOV_FLAG_ISML) + mov_write_isml_manifest(pb, mov); + + if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { + mov_write_moov_tag(pb, mov, s); + mov->fragments++; + } + + return 0; + error: + av_freep(&mov->tracks); + return -1; +} + +/** + * This function gets the moov size if moved to the top of the file: the chunk + * offset table can switch between stco (32-bit entries) to co64 (64-bit + * entries) when the moov is moved to the top, so the size of the moov would + * change. It also updates the chunk offset tables. + */ +static int compute_moov_size(AVFormatContext *s) +{ + int i, moov_size, moov_size2; + MOVMuxContext *mov = s->priv_data; + + moov_size = get_moov_size(s); + if (moov_size < 0) + return moov_size; + + for (i = 0; i < mov->nb_streams; i++) + mov->tracks[i].data_offset += moov_size; + + moov_size2 = get_moov_size(s); + if (moov_size2 < 0) + return moov_size2; + + /* if the size changed, we just switched from stco to co64 and needs to + * update the offsets */ + if (moov_size2 != moov_size) + for (i = 0; i < mov->nb_streams; i++) + mov->tracks[i].data_offset += moov_size2 - moov_size; + + return moov_size2; +} + +static int shift_data(AVFormatContext *s) +{ + int ret = 0, moov_size; + MOVMuxContext *mov = s->priv_data; + int64_t pos, pos_end = avio_tell(s->pb); + uint8_t *buf, *read_buf[2]; + int read_buf_id = 0; + int read_size[2]; + AVIOContext *read_pb; + + moov_size = compute_moov_size(s); + if (moov_size < 0) + return moov_size; + + buf = av_malloc(moov_size * 2); + if (!buf) + return AVERROR(ENOMEM); + read_buf[0] = buf; + read_buf[1] = buf + moov_size; + + /* Shift the data: the AVIO context of the output can only be used for + * writing, so we re-open the same output, but for reading. It also avoids + * a read/seek/write/seek back and forth. */ + avio_flush(s->pb); + ret = avio_open(&read_pb, s->filename, AVIO_FLAG_READ); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for " + "the second pass (faststart)\n", s->filename); + goto end; + } + + /* mark the end of the shift to up to the last data we wrote, and get ready + * for writing */ + pos_end = avio_tell(s->pb); + avio_seek(s->pb, mov->reserved_moov_pos + moov_size, SEEK_SET); + + /* start reading at where the new moov will be placed */ + avio_seek(read_pb, mov->reserved_moov_pos, SEEK_SET); + pos = avio_tell(read_pb); + +#define READ_BLOCK do { \ + read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], moov_size); \ + read_buf_id ^= 1; \ +} while (0) + + /* shift data by chunk of at most moov_size */ + READ_BLOCK; + do { + int n; + READ_BLOCK; + n = read_size[read_buf_id]; + if (n <= 0) + break; + avio_write(s->pb, read_buf[read_buf_id], n); + pos += n; + } while (pos < pos_end); + avio_close(read_pb); + +end: + av_free(buf); + return ret; +} + +static int mov_write_trailer(AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + AVIOContext *pb = s->pb; + int64_t moov_pos; + int res = 0; + int i; + + /* + * Before actually writing the trailer, make sure that there are no + * dangling subtitles, that need a terminating sample. + */ + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + if (trk->enc->codec_id == AV_CODEC_ID_MOV_TEXT && + !trk->last_sample_is_subtitle_end) { + mov_write_subtitle_end_packet(s, i, trk->track_duration); + trk->last_sample_is_subtitle_end = 1; + } + } + + moov_pos = avio_tell(pb); + + if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) { + /* Write size of mdat tag */ + if (mov->mdat_size + 8 <= UINT32_MAX) { + avio_seek(pb, mov->mdat_pos, SEEK_SET); + avio_wb32(pb, mov->mdat_size + 8); + } else { + /* overwrite 'wide' placeholder atom */ + avio_seek(pb, mov->mdat_pos - 8, SEEK_SET); + /* special value: real atom size will be 64 bit value after + * tag field */ + avio_wb32(pb, 1); + ffio_wfourcc(pb, "mdat"); + avio_wb64(pb, mov->mdat_size + 16); + } + avio_seek(pb, mov->reserved_moov_size > 0 ? mov->reserved_moov_pos : moov_pos, SEEK_SET); + + if (mov->reserved_moov_size == -1) { + av_log(s, AV_LOG_INFO, "Starting second pass: moving header on top of the file\n"); + res = shift_data(s); + if (res == 0) { + avio_seek(s->pb, mov->reserved_moov_pos, SEEK_SET); + mov_write_moov_tag(pb, mov, s); + } + } else if (mov->reserved_moov_size > 0) { + int64_t size; + mov_write_moov_tag(pb, mov, s); + size = mov->reserved_moov_size - (avio_tell(pb) - mov->reserved_moov_pos); + if(size < 8){ + av_log(s, AV_LOG_ERROR, "reserved_moov_size is too small, needed %"PRId64" additional\n", 8-size); + return -1; + } + avio_wb32(pb, size); + ffio_wfourcc(pb, "free"); + for(i=0; ichapter_track) + av_freep(&mov->tracks[mov->chapter_track].enc); + + for (i=0; inb_streams; i++) { + if (mov->tracks[i].tag == MKTAG('r','t','p',' ')) + ff_mov_close_hinting(&mov->tracks[i]); + else if (mov->tracks[i].tag == MKTAG('t','m','c','d') && mov->nb_meta_tmcd) + av_freep(&mov->tracks[i].enc); + if (mov->flags & FF_MOV_FLAG_FRAGMENT && + mov->tracks[i].vc1_info.struct_offset && s->pb->seekable) { + int64_t off = avio_tell(pb); + uint8_t buf[7]; + if (mov_write_dvc1_structs(&mov->tracks[i], buf) >= 0) { + avio_seek(pb, mov->tracks[i].vc1_info.struct_offset, SEEK_SET); + avio_write(pb, buf, 7); + avio_seek(pb, off, SEEK_SET); + } + } + av_freep(&mov->tracks[i].cluster); + av_freep(&mov->tracks[i].frag_info); + + if (mov->tracks[i].vos_len) + av_free(mov->tracks[i].vos_data); + + } + + av_freep(&mov->tracks); + + return res; +} + +#if CONFIG_MOV_MUXER +MOV_CLASS(mov) +AVOutputFormat ff_mov_muxer = { + .name = "mov", + .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), + .extensions = "mov", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = CONFIG_LIBX264_ENCODER ? + AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ + ff_codec_movvideo_tags, ff_codec_movaudio_tags, 0 + }, + .priv_class = &mov_muxer_class, +}; +#endif +#if CONFIG_TGP_MUXER +MOV_CLASS(tgp) +AVOutputFormat ff_tgp_muxer = { + .name = "3gp", + .long_name = NULL_IF_CONFIG_SMALL("3GP (3GPP file format)"), + .extensions = "3gp", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AMR_NB, + .video_codec = AV_CODEC_ID_H263, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 }, + .priv_class = &tgp_muxer_class, +}; +#endif +#if CONFIG_MP4_MUXER +MOV_CLASS(mp4) +AVOutputFormat ff_mp4_muxer = { + .name = "mp4", + .long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"), + .mime_type = "application/mp4", + .extensions = "mp4", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = CONFIG_LIBX264_ENCODER ? + AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 }, + .priv_class = &mp4_muxer_class, +}; +#endif +#if CONFIG_PSP_MUXER +MOV_CLASS(psp) +AVOutputFormat ff_psp_muxer = { + .name = "psp", + .long_name = NULL_IF_CONFIG_SMALL("PSP MP4 (MPEG-4 Part 14)"), + .extensions = "mp4,psp", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = CONFIG_LIBX264_ENCODER ? + AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 }, + .priv_class = &psp_muxer_class, +}; +#endif +#if CONFIG_TG2_MUXER +MOV_CLASS(tg2) +AVOutputFormat ff_tg2_muxer = { + .name = "3g2", + .long_name = NULL_IF_CONFIG_SMALL("3GP2 (3GPP2 file format)"), + .extensions = "3g2", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AMR_NB, + .video_codec = AV_CODEC_ID_H263, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 }, + .priv_class = &tg2_muxer_class, +}; +#endif +#if CONFIG_IPOD_MUXER +MOV_CLASS(ipod) +AVOutputFormat ff_ipod_muxer = { + .name = "ipod", + .long_name = NULL_IF_CONFIG_SMALL("iPod H.264 MP4 (MPEG-4 Part 14)"), + .mime_type = "application/mp4", + .extensions = "m4v,m4a", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_H264, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ codec_ipod_tags, 0 }, + .priv_class = &ipod_muxer_class, +}; +#endif +#if CONFIG_ISMV_MUXER +MOV_CLASS(ismv) +AVOutputFormat ff_ismv_muxer = { + .name = "ismv", + .long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming)"), + .mime_type = "application/mp4", + .extensions = "ismv,isma", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_H264, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 }, + .priv_class = &ismv_muxer_class, +}; +#endif +#if CONFIG_F4V_MUXER +MOV_CLASS(f4v) +AVOutputFormat ff_f4v_muxer = { + .name = "f4v", + .long_name = NULL_IF_CONFIG_SMALL("F4V Adobe Flash Video"), + .mime_type = "application/f4v", + .extensions = "f4v", + .priv_data_size = sizeof(MOVMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_H264, + .write_header = mov_write_header, + .write_packet = mov_write_packet, + .write_trailer = mov_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, + .codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, + .priv_class = &f4v_muxer_class, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/movenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,35 @@ +libavformat/movenc.o libavformat/movenc.o: libavformat/movenc.c \ + libavformat/movenc.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/riff.h libavformat/internal.h \ + libavformat/metadata.h libavformat/isom.h libavformat/dv.h \ + libavformat/avc.h libavcodec/get_bits.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/put_bits.h libavutil/bswap.h libavcodec/vc1.h \ + libavcodec/avcodec.h libavcodec/h264chroma.h libavcodec/mpegvideo.h \ + libavcodec/dsputil.h libavcodec/rnd_avg.h libavcodec/error_resilience.h \ + libavcodec/get_bits.h libavcodec/hpeldsp.h libavcodec/put_bits.h \ + libavcodec/ratecontrol.h libavutil/eval.h libavcodec/parser.h \ + libavcodec/mpeg12data.h libavcodec/rl.h libavcodec/thread.h \ + libavcodec/videodsp.h libavutil/opt.h libavutil/timecode.h \ + libavcodec/intrax8.h libavcodec/intrax8dsp.h libavcodec/vc1dsp.h \ + libavutil/avstring.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/opt.h libavformat/rtpenc.h libavformat/rtp.h \ + libavformat/avformat.h libavformat/mov_chan.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/movenc.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,190 @@ +/* + * MOV, 3GP, MP4 muxer + * Copyright (c) 2003 Thomas Raivio + * Copyright (c) 2004 Gildas Bazin + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_MOVENC_H +#define AVFORMAT_MOVENC_H + +#include "avformat.h" + +#define MOV_INDEX_CLUSTER_SIZE 16384 +#define MOV_TIMESCALE 1000 + +#define RTP_MAX_PACKET_SIZE 1450 + +#define MODE_MP4 0x01 +#define MODE_MOV 0x02 +#define MODE_3GP 0x04 +#define MODE_PSP 0x08 // example working PSP command line: +// ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4 +#define MODE_3G2 0x10 +#define MODE_IPOD 0x20 +#define MODE_ISM 0x40 +#define MODE_F4V 0x80 + +typedef struct MOVIentry { + uint64_t pos; + int64_t dts; + unsigned int size; + unsigned int samples_in_chunk; + unsigned int chunkNum; ///< Chunk number if the current entry is a chunk start otherwise 0 + unsigned int entries; + int cts; +#define MOV_SYNC_SAMPLE 0x0001 +#define MOV_PARTIAL_SYNC_SAMPLE 0x0002 + uint32_t flags; +} MOVIentry; + +typedef struct HintSample { + uint8_t *data; + int size; + int sample_number; + int offset; + int own_data; +} HintSample; + +typedef struct HintSampleQueue { + int size; + int len; + HintSample *samples; +} HintSampleQueue; + +typedef struct MOVFragmentInfo { + int64_t offset; + int64_t time; + int64_t duration; + int64_t tfrf_offset; +} MOVFragmentInfo; + +typedef struct MOVIndex { + int mode; + int entry; + unsigned timescale; + uint64_t time; + int64_t track_duration; + int last_sample_is_subtitle_end; + long sample_count; + long sample_size; + long chunkCount; + int has_keyframes; +#define MOV_TRACK_CTTS 0x0001 +#define MOV_TRACK_STPS 0x0002 + uint32_t flags; +#define MOV_TIMECODE_FLAG_DROPFRAME 0x0001 +#define MOV_TIMECODE_FLAG_24HOURSMAX 0x0002 +#define MOV_TIMECODE_FLAG_ALLOWNEGATIVE 0x0004 + uint32_t timecode_flags; + int language; + int secondary; + int track_id; + int tag; ///< stsd fourcc + AVCodecContext *enc; + + int vos_len; + uint8_t *vos_data; + MOVIentry *cluster; + int audio_vbr; + int height; ///< active picture (w/o VBI) height for D-10/IMX + uint32_t tref_tag; + int tref_id; ///< trackID of the referenced track + int64_t start_dts; + + int hint_track; ///< the track that hints this track, -1 if no hint track is set + int src_track; ///< the track that this hint (or tmcd) track describes + AVFormatContext *rtp_ctx; ///< the format context for the hinting rtp muxer + uint32_t prev_rtp_ts; + int64_t cur_rtp_ts_unwrapped; + uint32_t max_packet_size; + + int64_t default_duration; + uint32_t default_sample_flags; + uint32_t default_size; + + HintSampleQueue sample_queue; + + AVIOContext *mdat_buf; + int64_t moof_size_offset; + int64_t data_offset; + int64_t frag_start; + int64_t tfrf_offset; + + int nb_frag_info; + MOVFragmentInfo *frag_info; + + struct { + int64_t struct_offset; + int first_packet_seq; + int first_packet_entry; + int packet_seq; + int packet_entry; + int slices; + } vc1_info; +} MOVTrack; + +typedef struct MOVMuxContext { + const AVClass *av_class; + int mode; + int64_t time; + int nb_streams; + int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy) + int chapter_track; ///< qt chapter track number + int64_t mdat_pos; + uint64_t mdat_size; + MOVTrack *tracks; + + int flags; + int rtp_flags; + int reserved_moov_size; ///< 0 for disabled, -1 for automatic, size otherwise + int64_t reserved_moov_pos; + + int iods_skip; + int iods_video_profile; + int iods_audio_profile; + + int fragments; + int max_fragment_duration; + int min_fragment_duration; + int max_fragment_size; + int ism_lookahead; + AVIOContext *mdat_buf; + + int use_editlist; +} MOVMuxContext; + +#define FF_MOV_FLAG_RTP_HINT 1 +#define FF_MOV_FLAG_FRAGMENT 2 +#define FF_MOV_FLAG_EMPTY_MOOV 4 +#define FF_MOV_FLAG_FRAG_KEYFRAME 8 +#define FF_MOV_FLAG_SEPARATE_MOOF 16 +#define FF_MOV_FLAG_FRAG_CUSTOM 32 +#define FF_MOV_FLAG_ISML 64 +#define FF_MOV_FLAG_FASTSTART 128 + +int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); + +int ff_mov_init_hinting(AVFormatContext *s, int index, int src_index); +int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt, + int track_index, int sample, + uint8_t *sample_data, int sample_size); +void ff_mov_close_hinting(MOVTrack *track); + +#endif /* AVFORMAT_MOVENC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenc.o Binary file ffmpeg/libavformat/movenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenchint.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/movenchint.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,462 @@ +/* + * MOV, 3GP, MP4 muxer RTP hinting + * Copyright (c) 2010 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "movenc.h" +#include "libavutil/intreadwrite.h" +#include "internal.h" +#include "rtpenc_chain.h" +#include "avio_internal.h" +#include "rtp.h" + +int ff_mov_init_hinting(AVFormatContext *s, int index, int src_index) +{ + MOVMuxContext *mov = s->priv_data; + MOVTrack *track = &mov->tracks[index]; + MOVTrack *src_track = &mov->tracks[src_index]; + AVStream *src_st = s->streams[src_index]; + int ret = AVERROR(ENOMEM); + + track->tag = MKTAG('r','t','p',' '); + track->src_track = src_index; + + track->enc = avcodec_alloc_context3(NULL); + if (!track->enc) + goto fail; + track->enc->codec_type = AVMEDIA_TYPE_DATA; + track->enc->codec_tag = track->tag; + + ret = ff_rtp_chain_mux_open(&track->rtp_ctx, s, src_st, NULL, + RTP_MAX_PACKET_SIZE, src_index); + if (ret < 0) + goto fail; + + /* Copy the RTP AVStream timebase back to the hint AVStream */ + track->timescale = track->rtp_ctx->streams[0]->time_base.den; + + /* Mark the hinted track that packets written to it should be + * sent to this track for hinting. */ + src_track->hint_track = index; + return 0; +fail: + av_log(s, AV_LOG_WARNING, + "Unable to initialize hinting of stream %d\n", src_index); + av_freep(&track->enc); + /* Set a default timescale, to avoid crashes in av_dump_format */ + track->timescale = 90000; + return ret; +} + +/** + * Remove the first sample from the sample queue. + */ +static void sample_queue_pop(HintSampleQueue *queue) +{ + if (queue->len <= 0) + return; + if (queue->samples[0].own_data) + av_free(queue->samples[0].data); + queue->len--; + memmove(queue->samples, queue->samples + 1, sizeof(HintSample)*queue->len); +} + +/** + * Empty the sample queue, releasing all memory. + */ +static void sample_queue_free(HintSampleQueue *queue) +{ + int i; + for (i = 0; i < queue->len; i++) + if (queue->samples[i].own_data) + av_free(queue->samples[i].data); + av_freep(&queue->samples); + queue->len = 0; + queue->size = 0; +} + +/** + * Add a reference to the sample data to the sample queue. The data is + * not copied. sample_queue_retain should be called before pkt->data + * is reused/freed. + */ +static void sample_queue_push(HintSampleQueue *queue, uint8_t *data, int size, + int sample) +{ + /* No need to keep track of smaller samples, since describing them + * with immediates is more efficient. */ + if (size <= 14) + return; + if (!queue->samples || queue->len >= queue->size) { + HintSample* samples; + queue->size += 10; + samples = av_realloc(queue->samples, sizeof(HintSample)*queue->size); + if (!samples) + return; + queue->samples = samples; + } + queue->samples[queue->len].data = data; + queue->samples[queue->len].size = size; + queue->samples[queue->len].sample_number = sample; + queue->samples[queue->len].offset = 0; + queue->samples[queue->len].own_data = 0; + queue->len++; +} + +/** + * Make local copies of all referenced sample data in the queue. + */ +static void sample_queue_retain(HintSampleQueue *queue) +{ + int i; + for (i = 0; i < queue->len; ) { + HintSample *sample = &queue->samples[i]; + if (!sample->own_data) { + uint8_t* ptr = av_malloc(sample->size); + if (!ptr) { + /* Unable to allocate memory for this one, remove it */ + memmove(queue->samples + i, queue->samples + i + 1, + sizeof(HintSample)*(queue->len - i - 1)); + queue->len--; + continue; + } + memcpy(ptr, sample->data, sample->size); + sample->data = ptr; + sample->own_data = 1; + } + i++; + } +} + +/** + * Find matches of needle[n_pos ->] within haystack. If a sufficiently + * large match is found, matching bytes before n_pos are included + * in the match, too (within the limits of the arrays). + * + * @param haystack buffer that may contain parts of needle + * @param h_len length of the haystack buffer + * @param needle buffer containing source data that have been used to + * construct haystack + * @param n_pos start position in needle used for looking for matches + * @param n_len length of the needle buffer + * @param match_h_offset_ptr offset of the first matching byte within haystack + * @param match_n_offset_ptr offset of the first matching byte within needle + * @param match_len_ptr length of the matched segment + * @return 0 if a match was found, < 0 if no match was found + */ +static int match_segments(const uint8_t *haystack, int h_len, + const uint8_t *needle, int n_pos, int n_len, + int *match_h_offset_ptr, int *match_n_offset_ptr, + int *match_len_ptr) +{ + int h_pos; + for (h_pos = 0; h_pos < h_len; h_pos++) { + int match_len = 0; + int match_h_pos, match_n_pos; + + /* Check how many bytes match at needle[n_pos] and haystack[h_pos] */ + while (h_pos + match_len < h_len && n_pos + match_len < n_len && + needle[n_pos + match_len] == haystack[h_pos + match_len]) + match_len++; + if (match_len <= 8) + continue; + + /* If a sufficiently large match was found, try to expand + * the matched segment backwards. */ + match_h_pos = h_pos; + match_n_pos = n_pos; + while (match_n_pos > 0 && match_h_pos > 0 && + needle[match_n_pos - 1] == haystack[match_h_pos - 1]) { + match_n_pos--; + match_h_pos--; + match_len++; + } + if (match_len <= 14) + continue; + *match_h_offset_ptr = match_h_pos; + *match_n_offset_ptr = match_n_pos; + *match_len_ptr = match_len; + return 0; + } + return -1; +} + +/** + * Look for segments in samples in the sample queue matching the data + * in ptr. Samples not matching are removed from the queue. If a match + * is found, the next time it will look for matches starting from the + * end of the previous matched segment. + * + * @param data data to find matches for in the sample queue + * @param len length of the data buffer + * @param queue samples used for looking for matching segments + * @param pos the offset in data of the matched segment + * @param match_sample the number of the sample that contained the match + * @param match_offset the offset of the matched segment within the sample + * @param match_len the length of the matched segment + * @return 0 if a match was found, < 0 if no match was found + */ +static int find_sample_match(const uint8_t *data, int len, + HintSampleQueue *queue, int *pos, + int *match_sample, int *match_offset, + int *match_len) +{ + while (queue->len > 0) { + HintSample *sample = &queue->samples[0]; + /* If looking for matches in a new sample, skip the first 5 bytes, + * since they often may be modified/removed in the output packet. */ + if (sample->offset == 0 && sample->size > 5) + sample->offset = 5; + + if (match_segments(data, len, sample->data, sample->offset, + sample->size, pos, match_offset, match_len) == 0) { + *match_sample = sample->sample_number; + /* Next time, look for matches at this offset, with a little + * margin to this match. */ + sample->offset = *match_offset + *match_len + 5; + if (sample->offset + 10 >= sample->size) + sample_queue_pop(queue); /* Not enough useful data left */ + return 0; + } + + if (sample->offset < 10 && sample->size > 20) { + /* No match found from the start of the sample, + * try from the middle of the sample instead. */ + sample->offset = sample->size/2; + } else { + /* No match for this sample, remove it */ + sample_queue_pop(queue); + } + } + return -1; +} + +static void output_immediate(const uint8_t *data, int size, + AVIOContext *out, int *entries) +{ + while (size > 0) { + int len = size; + if (len > 14) + len = 14; + avio_w8(out, 1); /* immediate constructor */ + avio_w8(out, len); /* amount of valid data */ + avio_write(out, data, len); + data += len; + size -= len; + + for (; len < 14; len++) + avio_w8(out, 0); + + (*entries)++; + } +} + +static void output_match(AVIOContext *out, int match_sample, + int match_offset, int match_len, int *entries) +{ + avio_w8(out, 2); /* sample constructor */ + avio_w8(out, 0); /* track reference */ + avio_wb16(out, match_len); + avio_wb32(out, match_sample); + avio_wb32(out, match_offset); + avio_wb16(out, 1); /* bytes per block */ + avio_wb16(out, 1); /* samples per block */ + (*entries)++; +} + +static void describe_payload(const uint8_t *data, int size, + AVIOContext *out, int *entries, + HintSampleQueue *queue) +{ + /* Describe the payload using different constructors */ + while (size > 0) { + int match_sample, match_offset, match_len, pos; + if (find_sample_match(data, size, queue, &pos, &match_sample, + &match_offset, &match_len) < 0) + break; + output_immediate(data, pos, out, entries); + data += pos; + size -= pos; + output_match(out, match_sample, match_offset, match_len, entries); + data += match_len; + size -= match_len; + } + output_immediate(data, size, out, entries); +} + +/** + * Write an RTP hint (that may contain one or more RTP packets) + * for the packets in data. data contains one or more packets with a + * BE32 size header. + * + * @param out buffer where the hints are written + * @param data buffer containing RTP packets + * @param size the size of the data buffer + * @param trk the MOVTrack for the hint track + * @param pts pointer where the timestamp for the written RTP hint is stored + * @return the number of RTP packets in the written hint + */ +static int write_hint_packets(AVIOContext *out, const uint8_t *data, + int size, MOVTrack *trk, int64_t *pts) +{ + int64_t curpos; + int64_t count_pos, entries_pos; + int count = 0, entries; + + count_pos = avio_tell(out); + /* RTPsample header */ + avio_wb16(out, 0); /* packet count */ + avio_wb16(out, 0); /* reserved */ + + while (size > 4) { + uint32_t packet_len = AV_RB32(data); + uint16_t seq; + uint32_t ts; + + data += 4; + size -= 4; + if (packet_len > size || packet_len <= 12) + break; + if (RTP_PT_IS_RTCP(data[1])) { + /* RTCP packet, just skip */ + data += packet_len; + size -= packet_len; + continue; + } + + if (packet_len > trk->max_packet_size) + trk->max_packet_size = packet_len; + + seq = AV_RB16(&data[2]); + ts = AV_RB32(&data[4]); + + if (trk->prev_rtp_ts == 0) + trk->prev_rtp_ts = ts; + /* Unwrap the 32-bit RTP timestamp that wraps around often + * into a not (as often) wrapping 64-bit timestamp. */ + trk->cur_rtp_ts_unwrapped += (int32_t) (ts - trk->prev_rtp_ts); + trk->prev_rtp_ts = ts; + if (*pts == AV_NOPTS_VALUE) + *pts = trk->cur_rtp_ts_unwrapped; + + count++; + /* RTPpacket header */ + avio_wb32(out, 0); /* relative_time */ + avio_write(out, data, 2); /* RTP header */ + avio_wb16(out, seq); /* RTPsequenceseed */ + avio_wb16(out, 0); /* reserved + flags */ + entries_pos = avio_tell(out); + avio_wb16(out, 0); /* entry count */ + + data += 12; + size -= 12; + packet_len -= 12; + + entries = 0; + /* Write one or more constructors describing the payload data */ + describe_payload(data, packet_len, out, &entries, &trk->sample_queue); + data += packet_len; + size -= packet_len; + + curpos = avio_tell(out); + avio_seek(out, entries_pos, SEEK_SET); + avio_wb16(out, entries); + avio_seek(out, curpos, SEEK_SET); + } + + curpos = avio_tell(out); + avio_seek(out, count_pos, SEEK_SET); + avio_wb16(out, count); + avio_seek(out, curpos, SEEK_SET); + return count; +} + +int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt, + int track_index, int sample, + uint8_t *sample_data, int sample_size) +{ + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk = &mov->tracks[track_index]; + AVFormatContext *rtp_ctx = trk->rtp_ctx; + uint8_t *buf = NULL; + int size; + AVIOContext *hintbuf = NULL; + AVPacket hint_pkt; + int ret = 0, count; + + if (!rtp_ctx) + return AVERROR(ENOENT); + if (!rtp_ctx->pb) + return AVERROR(ENOMEM); + + if (sample_data) + sample_queue_push(&trk->sample_queue, sample_data, sample_size, sample); + else + sample_queue_push(&trk->sample_queue, pkt->data, pkt->size, sample); + + /* Feed the packet to the RTP muxer */ + ff_write_chained(rtp_ctx, 0, pkt, s); + + /* Fetch the output from the RTP muxer, open a new output buffer + * for next time. */ + size = avio_close_dyn_buf(rtp_ctx->pb, &buf); + if ((ret = ffio_open_dyn_packet_buf(&rtp_ctx->pb, + RTP_MAX_PACKET_SIZE)) < 0) + goto done; + + if (size <= 0) + goto done; + + /* Open a buffer for writing the hint */ + if ((ret = avio_open_dyn_buf(&hintbuf)) < 0) + goto done; + av_init_packet(&hint_pkt); + count = write_hint_packets(hintbuf, buf, size, trk, &hint_pkt.dts); + av_freep(&buf); + + /* Write the hint data into the hint track */ + hint_pkt.size = size = avio_close_dyn_buf(hintbuf, &buf); + hint_pkt.data = buf; + hint_pkt.pts = hint_pkt.dts; + hint_pkt.stream_index = track_index; + if (pkt->flags & AV_PKT_FLAG_KEY) + hint_pkt.flags |= AV_PKT_FLAG_KEY; + if (count > 0) + ff_mov_write_packet(s, &hint_pkt); +done: + av_free(buf); + sample_queue_retain(&trk->sample_queue); + return ret; +} + +void ff_mov_close_hinting(MOVTrack *track) { + AVFormatContext* rtp_ctx = track->rtp_ctx; + uint8_t *ptr; + + av_freep(&track->enc); + sample_queue_free(&track->sample_queue); + if (!rtp_ctx) + return; + if (rtp_ctx->pb) { + av_write_trailer(rtp_ctx); + avio_close_dyn_buf(rtp_ctx->pb, &ptr); + av_free(ptr); + } + avformat_free_context(rtp_ctx); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenchint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/movenchint.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/movenchint.o libavformat/movenchint.o: \ + libavformat/movenchint.c libavformat/movenc.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/internal.h libavformat/rtpenc_chain.h libavformat/url.h \ + libavformat/avio_internal.h libavformat/rtp.h libavformat/avformat.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/movenchint.o Binary file ffmpeg/libavformat/movenchint.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mp3dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,329 @@ +/* + * MP3 demuxer + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "internal.h" +#include "id3v2.h" +#include "id3v1.h" +#include "libavcodec/mpegaudiodecheader.h" + +#define XING_FLAG_FRAMES 0x01 +#define XING_FLAG_SIZE 0x02 +#define XING_FLAG_TOC 0x04 + +#define XING_TOC_COUNT 100 + +typedef struct { + int64_t filesize; + int xing_toc; + int start_pad; + int end_pad; +} MP3DecContext; + +/* mp3 read */ + +static int mp3_read_probe(AVProbeData *p) +{ + int max_frames, first_frames = 0; + int fsize, frames, sample_rate; + uint32_t header; + const uint8_t *buf, *buf0, *buf2, *end; + AVCodecContext avctx; + + buf0 = p->buf; + end = p->buf + p->buf_size - sizeof(uint32_t); + while(buf0 < end && !*buf0) + buf0++; + + max_frames = 0; + buf = buf0; + + for(; buf < end; buf= buf2+1) { + buf2 = buf; + + for(frames = 0; buf2 < end; frames++) { + header = AV_RB32(buf2); + fsize = avpriv_mpa_decode_header(&avctx, header, &sample_rate, &sample_rate, &sample_rate, &sample_rate); + if(fsize < 0) + break; + buf2 += fsize; + } + max_frames = FFMAX(max_frames, frames); + if(buf == buf0) + first_frames= frames; + } + // keep this in sync with ac3 probe, both need to avoid + // issues with MPEG-files! + if (first_frames>=4) return AVPROBE_SCORE_MAX/2+1; + else if(max_frames>200)return AVPROBE_SCORE_MAX/2; + else if(max_frames>=4) return AVPROBE_SCORE_MAX/4; + else if(ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size) + return AVPROBE_SCORE_MAX/8; + else if(max_frames>=1) return 1; + else return 0; +//mpegps_mp3_unrecognized_format.mpg has max_frames=3 +} + +static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration) +{ + int i; + MP3DecContext *mp3 = s->priv_data; + + if (!filesize && + !(filesize = avio_size(s->pb))) { + av_log(s, AV_LOG_WARNING, "Cannot determine file size, skipping TOC table.\n"); + return; + } + + for (i = 0; i < XING_TOC_COUNT; i++) { + uint8_t b = avio_r8(s->pb); + + av_add_index_entry(s->streams[0], + av_rescale(b, filesize, 256), + av_rescale(i, duration, XING_TOC_COUNT), + 0, 0, AVINDEX_KEYFRAME); + } + mp3->xing_toc = 1; +} + +/** + * Try to find Xing/Info/VBRI tags and compute duration from info therein + */ +static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) +{ + MP3DecContext *mp3 = s->priv_data; + uint32_t v, spf; + unsigned frames = 0; /* Total number of frames in file */ + unsigned size = 0; /* Total number of bytes in the stream */ + const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; + MPADecodeHeader c; + int vbrtag_size = 0; + int is_cbr; + + v = avio_rb32(s->pb); + if(ff_mpa_check_header(v) < 0) + return -1; + + if (avpriv_mpegaudio_decode_header(&c, v) == 0) + vbrtag_size = c.frame_size; + if(c.layer != 3) + return -1; + + spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */ + + /* Check for Xing / Info tag */ + avio_skip(s->pb, xing_offtbl[c.lsf == 1][c.nb_channels == 1]); + v = avio_rb32(s->pb); + is_cbr = v == MKBETAG('I', 'n', 'f', 'o'); + if (v == MKBETAG('X', 'i', 'n', 'g') || is_cbr) { + v = avio_rb32(s->pb); + if(v & XING_FLAG_FRAMES) + frames = avio_rb32(s->pb); + if(v & XING_FLAG_SIZE) + size = avio_rb32(s->pb); + if (v & XING_FLAG_TOC && frames) + read_xing_toc(s, size, av_rescale_q(frames, (AVRational){spf, c.sample_rate}, + st->time_base)); + if(v & 8) + avio_skip(s->pb, 4); + + v = avio_rb32(s->pb); + if(v == MKBETAG('L', 'A', 'M', 'E') || v == MKBETAG('L', 'a', 'v', 'f')) { + avio_skip(s->pb, 21-4); + v= avio_rb24(s->pb); + mp3->start_pad = v>>12; + mp3-> end_pad = v&4095; + st->skip_samples = mp3->start_pad + 528 + 1; + av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3-> end_pad); + } + } + + /* Check for VBRI tag (always 32 bytes after end of mpegaudio header) */ + avio_seek(s->pb, base + 4 + 32, SEEK_SET); + v = avio_rb32(s->pb); + if(v == MKBETAG('V', 'B', 'R', 'I')) { + /* Check tag version */ + if(avio_rb16(s->pb) == 1) { + /* skip delay and quality */ + avio_skip(s->pb, 4); + size = avio_rb32(s->pb); + frames = avio_rb32(s->pb); + } + } + + if(!frames && !size) + return -1; + + /* Skip the vbr tag frame */ + avio_seek(s->pb, base + vbrtag_size, SEEK_SET); + + if(frames) + st->duration = av_rescale_q(frames, (AVRational){spf, c.sample_rate}, + st->time_base); + if (size && frames && !is_cbr) + st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf); + + return 0; +} + +static int mp3_read_header(AVFormatContext *s) +{ + MP3DecContext *mp3 = s->priv_data; + AVStream *st; + int64_t off; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MP3; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + st->start_time = 0; + + // lcm of all mp3 sample rates + avpriv_set_pts_info(st, 64, 1, 14112000); + + s->pb->maxsize = -1; + off = avio_tell(s->pb); + + if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) + ff_id3v1_read(s); + + if(s->pb->seekable) + mp3->filesize = avio_size(s->pb); + + if (mp3_parse_vbr_tags(s, st, off) < 0) + avio_seek(s->pb, off, SEEK_SET); + + /* the parameters will be extracted from the compressed bitstream */ + return 0; +} + +#define MP3_PACKET_SIZE 1024 + +static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MP3DecContext *mp3 = s->priv_data; + int ret, size; + int64_t pos; + + size= MP3_PACKET_SIZE; + pos = avio_tell(s->pb); + if(mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize) + size= FFMIN(size, mp3->filesize - pos); + + ret= av_get_packet(s->pb, pkt, size); + if (ret <= 0) { + if(ret<0) + return ret; + return AVERROR_EOF; + } + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + + if (ret >= ID3v1_TAG_SIZE && + memcmp(&pkt->data[ret - ID3v1_TAG_SIZE], "TAG", 3) == 0) + ret -= ID3v1_TAG_SIZE; + + /* note: we need to modify the packet size here to handle the last + packet */ + pkt->size = ret; + return ret; +} + +static int check(AVFormatContext *s, int64_t pos) +{ + int64_t ret = avio_seek(s->pb, pos, SEEK_SET); + unsigned header; + MPADecodeHeader sd; + if (ret < 0) + return ret; + header = avio_rb32(s->pb); + if (ff_mpa_check_header(header) < 0) + return -1; + if (avpriv_mpegaudio_decode_header(&sd, header) == 1) + return -1; + return sd.frame_size; +} + +static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, + int flags) +{ + MP3DecContext *mp3 = s->priv_data; + AVIndexEntry *ie; + AVStream *st = s->streams[0]; + int64_t ret = av_index_search_timestamp(st, timestamp, flags); + int i, j; + + if (!mp3->xing_toc) { + st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0; + + return -1; + } + + if (ret < 0) + return ret; + + ie = &st->index_entries[ret]; + ret = avio_seek(s->pb, ie->pos, SEEK_SET); + if (ret < 0) + return ret; + +#define MIN_VALID 3 + for(i=0; i<4096; i++) { + int64_t pos = ie->pos + i; + for(j=0; jpb, ie->pos + i, SEEK_SET); + if (ret < 0) + return ret; + ff_update_cur_dts(s, st, ie->timestamp); + st->skip_samples = ie->timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0; + return 0; +} + +AVInputFormat ff_mp3_demuxer = { + .name = "mp3", + .long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"), + .read_probe = mp3_read_probe, + .read_header = mp3_read_header, + .read_packet = mp3_read_packet, + .read_seek = mp3_seek, + .priv_data_size = sizeof(MP3DecContext), + .flags = AVFMT_GENERIC_INDEX, + .extensions = "mp2,mp3,m2a", /* XXX: use probe */ +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mp3dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mp3dec.o libavformat/mp3dec.o: libavformat/mp3dec.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/dict.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/id3v2.h libavformat/metadata.h libavformat/id3v1.h \ + libavcodec/mpegaudiodecheader.h libavcodec/avcodec.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3dec.o Binary file ffmpeg/libavformat/mp3dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3enc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mp3enc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,522 @@ +/* + * MP3 muxer + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "avio_internal.h" +#include "id3v1.h" +#include "id3v2.h" +#include "rawenc.h" +#include "libavutil/avstring.h" +#include "libavcodec/mpegaudio.h" +#include "libavcodec/mpegaudiodata.h" +#include "libavcodec/mpegaudiodecheader.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavcodec/mpegaudio.h" +#include "libavcodec/mpegaudiodata.h" +#include "libavcodec/mpegaudiodecheader.h" +#include "libavformat/avio_internal.h" +#include "libavutil/dict.h" +#include "libavutil/avassert.h" + +static int id3v1_set_string(AVFormatContext *s, const char *key, + uint8_t *buf, int buf_size) +{ + AVDictionaryEntry *tag; + if ((tag = av_dict_get(s->metadata, key, NULL, 0))) + av_strlcpy(buf, tag->value, buf_size); + return !!tag; +} + +static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) +{ + AVDictionaryEntry *tag; + int i, count = 0; + + memset(buf, 0, ID3v1_TAG_SIZE); /* fail safe */ + buf[0] = 'T'; + buf[1] = 'A'; + buf[2] = 'G'; + /* we knowingly overspecify each tag length by one byte to compensate for the mandatory null byte added by av_strlcpy */ + count += id3v1_set_string(s, "TIT2", buf + 3, 30 + 1); //title + count += id3v1_set_string(s, "TPE1", buf + 33, 30 + 1); //author|artist + count += id3v1_set_string(s, "TALB", buf + 63, 30 + 1); //album + count += id3v1_set_string(s, "TDRL", buf + 93, 4 + 1); //date + count += id3v1_set_string(s, "comment", buf + 97, 30 + 1); + if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track + buf[125] = 0; + buf[126] = atoi(tag->value); + count++; + } + buf[127] = 0xFF; /* default to unknown genre */ + if ((tag = av_dict_get(s->metadata, "TCON", NULL, 0))) { //genre + for(i = 0; i <= ID3v1_GENRE_MAX; i++) { + if (!av_strcasecmp(tag->value, ff_id3v1_genre_str[i])) { + buf[127] = i; + count++; + break; + } + } + } + return count; +} + +#define XING_NUM_BAGS 400 +#define XING_TOC_SIZE 100 +// maximum size of the xing frame: offset/Xing/flags/frames/size/TOC +#define XING_MAX_SIZE (32 + 4 + 4 + 4 + 4 + XING_TOC_SIZE) + +typedef struct MP3Context { + const AVClass *class; + ID3v2EncContext id3; + int id3v2_version; + int write_id3v1; + + /* xing header */ + int64_t xing_offset; + int32_t frames; + int32_t size; + uint32_t want; + uint32_t seen; + uint32_t pos; + uint64_t bag[XING_NUM_BAGS]; + int initial_bitrate; + int has_variable_bitrate; + + /* index of the audio stream */ + int audio_stream_idx; + /* number of attached pictures we still need to write */ + int pics_to_write; + + /* audio packets are queued here until we get all the attached pictures */ + AVPacketList *queue, *queue_end; +} MP3Context; + +static const uint8_t xing_offtbl[2][2] = {{32, 17}, {17, 9}}; + +/* + * Write an empty XING header and initialize respective data. + */ +static int mp3_write_xing(AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec; + int bitrate_idx; + int best_bitrate_idx = -1; + int best_bitrate_error= INT_MAX; + int xing_offset; + int32_t header, mask; + MPADecodeHeader c; + int srate_idx, ver = 0, i, channels; + int needed; + const char *vendor = (codec->flags & CODEC_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT; + + if (!s->pb->seekable) + return 0; + + for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) { + const uint16_t base_freq = avpriv_mpa_freq_tab[i]; + + if (codec->sample_rate == base_freq) ver = 0x3; // MPEG 1 + else if (codec->sample_rate == base_freq / 2) ver = 0x2; // MPEG 2 + else if (codec->sample_rate == base_freq / 4) ver = 0x0; // MPEG 2.5 + else continue; + + srate_idx = i; + break; + } + if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) { + av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing header.\n"); + return -1; + } + + switch (codec->channels) { + case 1: channels = MPA_MONO; break; + case 2: channels = MPA_STEREO; break; + default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, " + "not writing Xing header.\n"); + return -1; + } + + /* dummy MPEG audio header */ + header = 0xffU << 24; // sync + header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/ + header |= (srate_idx << 2) << 8; + header |= channels << 6; + + for (bitrate_idx=1; bitrate_idx<15; bitrate_idx++) { + int error; + avpriv_mpegaudio_decode_header(&c, header | (bitrate_idx << (4+8))); + error= FFABS(c.bit_rate - codec->bit_rate); + if(error < best_bitrate_error){ + best_bitrate_error= error; + best_bitrate_idx = bitrate_idx; + } + } + av_assert0(best_bitrate_idx >= 0); + + for (bitrate_idx= best_bitrate_idx;; bitrate_idx++) { + if (15 == bitrate_idx) + return -1; + mask = bitrate_idx << (4+8); + header |= mask; + avpriv_mpegaudio_decode_header(&c, header); + xing_offset=xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + needed = 4 // header + + xing_offset + + 4 // xing tag + + 4 // frames/size/toc flags + + 4 // frames + + 4 // size + + XING_TOC_SIZE // toc + + 24 + ; + + if (needed <= c.frame_size) + break; + header &= ~mask; + } + + avio_wb32(s->pb, header); + + ffio_fill(s->pb, 0, xing_offset); + mp3->xing_offset = avio_tell(s->pb); + ffio_wfourcc(s->pb, "Xing"); + avio_wb32(s->pb, 0x01 | 0x02 | 0x04); // frames / size / TOC + + mp3->size = c.frame_size; + mp3->want=1; + mp3->seen=0; + mp3->pos=0; + + avio_wb32(s->pb, 0); // frames + avio_wb32(s->pb, 0); // size + + // toc + for (i = 0; i < XING_TOC_SIZE; ++i) + avio_w8(s->pb, (uint8_t)(255 * i / XING_TOC_SIZE)); + + for (i = 0; i < strlen(vendor); ++i) + avio_w8(s->pb, vendor[i]); + for (; i < 21; ++i) + avio_w8(s->pb, 0); + avio_wb24(s->pb, FFMAX(codec->delay - 528 - 1, 0)<<12); + + ffio_fill(s->pb, 0, c.frame_size - needed); + + return 0; +} + +/* + * Add a frame to XING data. + * Following lame's "VbrTag.c". + */ +static void mp3_xing_add_frame(MP3Context *mp3, AVPacket *pkt) +{ + int i; + + mp3->frames++; + mp3->seen++; + mp3->size += pkt->size; + + if (mp3->want == mp3->seen) { + mp3->bag[mp3->pos] = mp3->size; + + if (XING_NUM_BAGS == ++mp3->pos) { + /* shrink table to half size by throwing away each second bag. */ + for (i = 1; i < XING_NUM_BAGS; i += 2) + mp3->bag[i >> 1] = mp3->bag[i]; + + /* double wanted amount per bag. */ + mp3->want *= 2; + /* adjust current position to half of table size. */ + mp3->pos = XING_NUM_BAGS / 2; + } + + mp3->seen = 0; + } +} + +static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) +{ + MP3Context *mp3 = s->priv_data; + + if (pkt->data && pkt->size >= 4) { + MPADecodeHeader c; + int av_unused base; + uint32_t head = AV_RB32(pkt->data); + + if (ff_mpa_check_header(head) < 0) { + av_log(s, AV_LOG_WARNING, "Audio packet of size %d (starting with %08X...) " + "is invalid, writing it anyway.\n", pkt->size, head); + return ff_raw_write_packet(s, pkt); + } + avpriv_mpegaudio_decode_header(&c, head); + + if (!mp3->initial_bitrate) + mp3->initial_bitrate = c.bit_rate; + if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate)) + mp3->has_variable_bitrate = 1; + +#ifdef FILTER_VBR_HEADERS + /* filter out XING and INFO headers. */ + base = 4 + xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + + if (base + 4 <= pkt->size) { + uint32_t v = AV_RB32(pkt->data + base); + + if (MKBETAG('X','i','n','g') == v || MKBETAG('I','n','f','o') == v) + return 0; + } + + /* filter out VBRI headers. */ + base = 4 + 32; + + if (base + 4 <= pkt->size && MKBETAG('V','B','R','I') == AV_RB32(pkt->data + base)) + return 0; +#endif + + if (mp3->xing_offset) + mp3_xing_add_frame(mp3, pkt); + } + + return ff_raw_write_packet(s, pkt); +} + +static int mp3_queue_flush(AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + AVPacketList *pktl; + int ret = 0, write = 1; + + ff_id3v2_finish(&mp3->id3, s->pb); + mp3_write_xing(s); + + while ((pktl = mp3->queue)) { + if (write && (ret = mp3_write_audio_packet(s, &pktl->pkt)) < 0) + write = 0; + av_free_packet(&pktl->pkt); + mp3->queue = pktl->next; + av_freep(&pktl); + } + mp3->queue_end = NULL; + return ret; +} + +static void mp3_update_xing(AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + int i; + + /* replace "Xing" identification string with "Info" for CBR files. */ + if (!mp3->has_variable_bitrate) { + avio_seek(s->pb, mp3->xing_offset, SEEK_SET); + ffio_wfourcc(s->pb, "Info"); + } + + avio_seek(s->pb, mp3->xing_offset + 8, SEEK_SET); + avio_wb32(s->pb, mp3->frames); + avio_wb32(s->pb, mp3->size); + + avio_w8(s->pb, 0); // first toc entry has to be zero. + + for (i = 1; i < XING_TOC_SIZE; ++i) { + int j = i * mp3->pos / XING_TOC_SIZE; + int seek_point = 256LL * mp3->bag[j] / mp3->size; + avio_w8(s->pb, FFMIN(seek_point, 255)); + } + + avio_seek(s->pb, 0, SEEK_END); +} + +static int mp3_write_trailer(struct AVFormatContext *s) +{ + uint8_t buf[ID3v1_TAG_SIZE]; + MP3Context *mp3 = s->priv_data; + + if (mp3->pics_to_write) { + av_log(s, AV_LOG_WARNING, "No packets were sent for some of the " + "attached pictures.\n"); + mp3_queue_flush(s); + } + + /* write the id3v1 tag */ + if (mp3->write_id3v1 && id3v1_create_tag(s, buf) > 0) { + avio_write(s->pb, buf, ID3v1_TAG_SIZE); + } + + if (mp3->xing_offset) + mp3_update_xing(s); + + return 0; +} + +static int query_codec(enum AVCodecID id, int std_compliance) +{ + const CodecMime *cm= ff_id3v2_mime_tags; + while(cm->id != AV_CODEC_ID_NONE) { + if(id == cm->id) + return MKTAG('A', 'P', 'I', 'C'); + cm++; + } + return -1; +} + +#if CONFIG_MP2_MUXER +AVOutputFormat ff_mp2_muxer = { + .name = "mp2", + .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), + .mime_type = "audio/x-mpeg", + .extensions = "mp2,m2a", + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_MP3_MUXER + +static const AVOption options[] = { + { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", + offsetof(MP3Context, id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, AV_OPT_FLAG_ENCODING_PARAM}, + { "write_id3v1", "Enable ID3v1 writing. ID3v1 tags are written in UTF-8 which may not be supported by most software.", + offsetof(MP3Context, write_id3v1), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +static const AVClass mp3_muxer_class = { + .class_name = "MP3 muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + MP3Context *mp3 = s->priv_data; + + if (pkt->stream_index == mp3->audio_stream_idx) { + if (mp3->pics_to_write) { + /* buffer audio packets until we get all the pictures */ + AVPacketList *pktl = av_mallocz(sizeof(*pktl)); + if (!pktl) + return AVERROR(ENOMEM); + + pktl->pkt = *pkt; + pktl->pkt.buf = av_buffer_ref(pkt->buf); + if (!pktl->pkt.buf) { + av_freep(&pktl); + return AVERROR(ENOMEM); + } + + if (mp3->queue_end) + mp3->queue_end->next = pktl; + else + mp3->queue = pktl; + mp3->queue_end = pktl; + } else + return mp3_write_audio_packet(s, pkt); + } else { + int ret; + + /* warn only once for each stream */ + if (s->streams[pkt->stream_index]->nb_frames == 1) { + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + } + if (!mp3->pics_to_write || s->streams[pkt->stream_index]->nb_frames >= 1) + return 0; + + if ((ret = ff_id3v2_write_apic(s, &mp3->id3, pkt)) < 0) + return ret; + mp3->pics_to_write--; + + /* flush the buffered audio packets */ + if (!mp3->pics_to_write && + (ret = mp3_queue_flush(s)) < 0) + return ret; + } + + return 0; +} + +/** + * Write an ID3v2 header at beginning of stream + */ + +static int mp3_write_header(struct AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + int ret, i; + + /* check the streams -- we want exactly one audio and arbitrary number of + * video (attached pictures) */ + mp3->audio_stream_idx = -1; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (mp3->audio_stream_idx >= 0 || st->codec->codec_id != AV_CODEC_ID_MP3) { + av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one MP3 " + "audio stream is required.\n"); + return AVERROR(EINVAL); + } + mp3->audio_stream_idx = i; + } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) { + av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in MP3.\n"); + return AVERROR(EINVAL); + } + } + if (mp3->audio_stream_idx < 0) { + av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); + return AVERROR(EINVAL); + } + mp3->pics_to_write = s->nb_streams - 1; + + ff_id3v2_start(&mp3->id3, s->pb, mp3->id3v2_version, ID3v2_DEFAULT_MAGIC); + ret = ff_id3v2_write_metadata(s, &mp3->id3); + if (ret < 0) + return ret; + + if (!mp3->pics_to_write) { + ff_id3v2_finish(&mp3->id3, s->pb); + mp3_write_xing(s); + } + + return 0; +} + +AVOutputFormat ff_mp3_muxer = { + .name = "mp3", + .long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), + .mime_type = "audio/x-mpeg", + .extensions = "mp3", + .priv_data_size = sizeof(MP3Context), + .audio_codec = AV_CODEC_ID_MP3, + .video_codec = AV_CODEC_ID_PNG, + .write_header = mp3_write_header, + .write_packet = mp3_write_packet, + .write_trailer = mp3_write_trailer, + .query_codec = query_codec, + .flags = AVFMT_NOTIMESTAMPS, + .priv_class = &mp3_muxer_class, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3enc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mp3enc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,25 @@ +libavformat/mp3enc.o libavformat/mp3enc.o: libavformat/mp3enc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/id3v1.h libavformat/id3v2.h \ + libavformat/internal.h libavformat/metadata.h libavformat/rawenc.h \ + libavutil/avstring.h libavcodec/mpegaudio.h libavcodec/mpegaudiodata.h \ + libavutil/internal.h libavcodec/mpegaudiodecheader.h \ + libavcodec/avcodec.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/opt.h \ + libavformat/avio_internal.h libavutil/avassert.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mp3enc.o Binary file ffmpeg/libavformat/mp3enc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,237 @@ +/* + * Musepack demuxer + * Copyright (c) 2006 Konstantin Shishkov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "internal.h" +#include "apetag.h" +#include "id3v1.h" +#include "libavutil/dict.h" + +#define MPC_FRAMESIZE 1152 +#define DELAY_FRAMES 32 + +static const int mpc_rate[4] = { 44100, 48000, 37800, 32000 }; +typedef struct { + int64_t pos; + int size, skip; +}MPCFrame; + +typedef struct { + int ver; + uint32_t curframe, lastframe; + uint32_t fcount; + MPCFrame *frames; + int curbits; + int frames_noted; +} MPCContext; + +static int mpc_probe(AVProbeData *p) +{ + const uint8_t *d = p->buf; + if (d[0] == 'M' && d[1] == 'P' && d[2] == '+' && (d[3] == 0x17 || d[3] == 0x7)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int mpc_read_header(AVFormatContext *s) +{ + MPCContext *c = s->priv_data; + AVStream *st; + + if(avio_rl24(s->pb) != MKTAG('M', 'P', '+', 0)){ + av_log(s, AV_LOG_ERROR, "Not a Musepack file\n"); + return AVERROR_INVALIDDATA; + } + c->ver = avio_r8(s->pb); + if(c->ver != 0x07 && c->ver != 0x17){ + av_log(s, AV_LOG_ERROR, "Can demux Musepack SV7, got version %02X\n", c->ver); + return AVERROR_INVALIDDATA; + } + c->fcount = avio_rl32(s->pb); + if((int64_t)c->fcount * sizeof(MPCFrame) >= UINT_MAX){ + av_log(s, AV_LOG_ERROR, "Too many frames, seeking is not possible\n"); + return AVERROR_INVALIDDATA; + } + if(c->fcount){ + c->frames = av_malloc(c->fcount * sizeof(MPCFrame)); + if(!c->frames){ + av_log(s, AV_LOG_ERROR, "Cannot allocate seektable\n"); + return AVERROR(ENOMEM); + } + }else{ + av_log(s, AV_LOG_WARNING, "Container reports no frames\n"); + } + c->curframe = 0; + c->lastframe = -1; + c->curbits = 8; + c->frames_noted = 0; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MUSEPACK7; + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->bits_per_coded_sample = 16; + + st->codec->extradata_size = 16; + st->codec->extradata = av_mallocz(st->codec->extradata_size+FF_INPUT_BUFFER_PADDING_SIZE); + avio_read(s->pb, st->codec->extradata, 16); + st->codec->sample_rate = mpc_rate[st->codec->extradata[2] & 3]; + avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codec->sample_rate); + /* scan for seekpoints */ + st->start_time = 0; + st->duration = c->fcount; + + /* try to read APE tags */ + if (s->pb->seekable) { + int64_t pos = avio_tell(s->pb); + ff_ape_parse_tag(s); + if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) + ff_id3v1_read(s); + avio_seek(s->pb, pos, SEEK_SET); + } + + return 0; +} + +static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPCContext *c = s->priv_data; + int ret, size, size2, curbits, cur = c->curframe; + unsigned tmp; + int64_t pos; + + if (c->curframe >= c->fcount && c->fcount) + return AVERROR_EOF; + + if(c->curframe != c->lastframe + 1){ + avio_seek(s->pb, c->frames[c->curframe].pos, SEEK_SET); + c->curbits = c->frames[c->curframe].skip; + } + c->lastframe = c->curframe; + c->curframe++; + curbits = c->curbits; + pos = avio_tell(s->pb); + tmp = avio_rl32(s->pb); + if(curbits <= 12){ + size2 = (tmp >> (12 - curbits)) & 0xFFFFF; + }else{ + size2 = (tmp << (curbits - 12) | avio_rl32(s->pb) >> (44 - curbits)) & 0xFFFFF; + } + curbits += 20; + avio_seek(s->pb, pos, SEEK_SET); + + size = ((size2 + curbits + 31) & ~31) >> 3; + if(cur == c->frames_noted && c->fcount){ + c->frames[cur].pos = pos; + c->frames[cur].size = size; + c->frames[cur].skip = curbits - 20; + av_add_index_entry(s->streams[0], cur, cur, size, 0, AVINDEX_KEYFRAME); + c->frames_noted++; + } + c->curbits = (curbits + size2) & 0x1F; + + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; + + pkt->data[0] = curbits; + pkt->data[1] = (c->curframe > c->fcount) && c->fcount; + pkt->data[2] = 0; + pkt->data[3] = 0; + + pkt->stream_index = 0; + pkt->pts = cur; + ret = avio_read(s->pb, pkt->data + 4, size); + if(c->curbits) + avio_seek(s->pb, -4, SEEK_CUR); + if(ret < size){ + av_free_packet(pkt); + return ret < 0 ? ret : AVERROR(EIO); + } + pkt->size = ret + 4; + + return 0; +} + +static int mpc_read_close(AVFormatContext *s) +{ + MPCContext *c = s->priv_data; + + av_freep(&c->frames); + return 0; +} + +/** + * Seek to the given position + * If position is unknown but is within the limits of file + * then packets are skipped unless desired position is reached + * + * Also this function makes use of the fact that timestamp == frameno + */ +static int mpc_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVStream *st = s->streams[stream_index]; + MPCContext *c = s->priv_data; + AVPacket pkt1, *pkt = &pkt1; + int ret; + int index = av_index_search_timestamp(st, FFMAX(timestamp - DELAY_FRAMES, 0), flags); + uint32_t lastframe; + + /* if found, seek there */ + if (index >= 0 && st->index_entries[st->nb_index_entries-1].timestamp >= timestamp - DELAY_FRAMES){ + c->curframe = st->index_entries[index].pos; + return 0; + } + /* if timestamp is out of bounds, return error */ + if(timestamp < 0 || timestamp >= c->fcount) + return -1; + timestamp -= DELAY_FRAMES; + /* seek to the furthest known position and read packets until + we reach desired position */ + lastframe = c->curframe; + if(c->frames_noted) c->curframe = c->frames_noted - 1; + while(c->curframe < timestamp){ + ret = av_read_frame(s, pkt); + if (ret < 0){ + c->curframe = lastframe; + return ret; + } + av_free_packet(pkt); + } + return 0; +} + + +AVInputFormat ff_mpc_demuxer = { + .name = "mpc", + .long_name = NULL_IF_CONFIG_SMALL("Musepack"), + .priv_data_size = sizeof(MPCContext), + .read_probe = mpc_probe, + .read_header = mpc_read_header, + .read_packet = mpc_read_packet, + .read_close = mpc_read_close, + .read_seek = mpc_read_seek, + .extensions = "mpc", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/mpc.o libavformat/mpc.o: libavformat/mpc.c \ + libavutil/channel_layout.h libavcodec/get_bits.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/apetag.h \ + libavformat/id3v1.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc.o Binary file ffmpeg/libavformat/mpc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc8.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpc8.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,317 @@ +/* + * Musepack SV8 demuxer + * Copyright (c) 2007 Konstantin Shishkov + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/unary.h" +#include "apetag.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" + +/// Two-byte MPC tag +#define MKMPCTAG(a, b) (a | (b << 8)) + +#define TAG_MPCK MKTAG('M','P','C','K') + +/// Reserved MPC tags +enum MPCPacketTags{ + TAG_STREAMHDR = MKMPCTAG('S','H'), + TAG_STREAMEND = MKMPCTAG('S','E'), + + TAG_AUDIOPACKET = MKMPCTAG('A','P'), + + TAG_SEEKTBLOFF = MKMPCTAG('S','O'), + TAG_SEEKTABLE = MKMPCTAG('S','T'), + + TAG_REPLAYGAIN = MKMPCTAG('R','G'), + TAG_ENCINFO = MKMPCTAG('E','I'), +}; + +static const int mpc8_rate[8] = { 44100, 48000, 37800, 32000, -1, -1, -1, -1 }; + +typedef struct { + int ver; + int64_t header_pos; + int64_t samples; + + int64_t apetag_start; +} MPCContext; + +static inline int64_t bs_get_v(const uint8_t **bs) +{ + int64_t v = 0; + int br = 0; + int c; + + do { + c = **bs; (*bs)++; + v <<= 7; + v |= c & 0x7F; + br++; + if (br > 10) + return -1; + } while (c & 0x80); + + return v - br; +} + +static int mpc8_probe(AVProbeData *p) +{ + const uint8_t *bs = p->buf + 4; + const uint8_t *bs_end = bs + p->buf_size; + int64_t size; + + if (p->buf_size < 16) + return 0; + if (AV_RL32(p->buf) != TAG_MPCK) + return 0; + while (bs < bs_end + 3) { + int header_found = (bs[0] == 'S' && bs[1] == 'H'); + if (bs[0] < 'A' || bs[0] > 'Z' || bs[1] < 'A' || bs[1] > 'Z') + return 0; + bs += 2; + size = bs_get_v(&bs); + if (size < 2) + return 0; + if (bs + size - 2 >= bs_end) + return AVPROBE_SCORE_MAX / 4 - 1; //seems to be valid MPC but no header yet + if (header_found) { + if (size < 11 || size > 28) + return 0; + if (!AV_RL32(bs)) //zero CRC is invalid + return 0; + return AVPROBE_SCORE_MAX; + } else { + bs += size - 2; + } + } + return 0; +} + +static inline int64_t gb_get_v(GetBitContext *gb) +{ + int64_t v = 0; + int bits = 0; + while(get_bits1(gb) && bits < 64-7){ + v <<= 7; + v |= get_bits(gb, 7); + bits += 7; + } + v <<= 7; + v |= get_bits(gb, 7); + + return v; +} + +static void mpc8_get_chunk_header(AVIOContext *pb, int *tag, int64_t *size) +{ + int64_t pos; + pos = avio_tell(pb); + *tag = avio_rl16(pb); + *size = ffio_read_varlen(pb); + *size -= avio_tell(pb) - pos; +} + +static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) +{ + MPCContext *c = s->priv_data; + int tag; + int64_t size, pos, ppos[2]; + uint8_t *buf; + int i, t, seekd; + GetBitContext gb; + + if (s->nb_streams<=0) { + av_log(s, AV_LOG_ERROR, "cannot parse stream table before stream header\n"); + return; + } + + avio_seek(s->pb, off, SEEK_SET); + mpc8_get_chunk_header(s->pb, &tag, &size); + if(tag != TAG_SEEKTABLE){ + av_log(s, AV_LOG_ERROR, "No seek table at given position\n"); + return; + } + if (size > INT_MAX/10 || size<=0) { + av_log(s, AV_LOG_ERROR, "Seek table size is invalid\n"); + return; + } + if(!(buf = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE))) + return; + avio_read(s->pb, buf, size); + init_get_bits(&gb, buf, size * 8); + size = gb_get_v(&gb); + if(size > UINT_MAX/4 || size > c->samples/1152){ + av_log(s, AV_LOG_ERROR, "Seek table is too big\n"); + return; + } + seekd = get_bits(&gb, 4); + for(i = 0; i < 2; i++){ + pos = gb_get_v(&gb) + c->header_pos; + ppos[1 - i] = pos; + av_add_index_entry(s->streams[0], pos, i, 0, 0, AVINDEX_KEYFRAME); + } + for(; i < size; i++){ + t = get_unary(&gb, 1, 33) << 12; + t += get_bits(&gb, 12); + if(t & 1) + t = -(t & ~1); + pos = (t >> 1) + ppos[0]*2 - ppos[1]; + av_add_index_entry(s->streams[0], pos, i << seekd, 0, 0, AVINDEX_KEYFRAME); + ppos[1] = ppos[0]; + ppos[0] = pos; + } + av_free(buf); +} + +static void mpc8_handle_chunk(AVFormatContext *s, int tag, int64_t chunk_pos, int64_t size) +{ + AVIOContext *pb = s->pb; + int64_t pos, off; + + switch(tag){ + case TAG_SEEKTBLOFF: + pos = avio_tell(pb) + size; + off = ffio_read_varlen(pb); + mpc8_parse_seektable(s, chunk_pos + off); + avio_seek(pb, pos, SEEK_SET); + break; + default: + avio_skip(pb, size); + } +} + +static int mpc8_read_header(AVFormatContext *s) +{ + MPCContext *c = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + int tag = 0; + int64_t size, pos; + + c->header_pos = avio_tell(pb); + if(avio_rl32(pb) != TAG_MPCK){ + av_log(s, AV_LOG_ERROR, "Not a Musepack8 file\n"); + return AVERROR_INVALIDDATA; + } + + while(!url_feof(pb)){ + pos = avio_tell(pb); + mpc8_get_chunk_header(pb, &tag, &size); + if(tag == TAG_STREAMHDR) + break; + mpc8_handle_chunk(s, tag, pos, size); + } + if(tag != TAG_STREAMHDR){ + av_log(s, AV_LOG_ERROR, "Stream header not found\n"); + return AVERROR_INVALIDDATA; + } + pos = avio_tell(pb); + avio_skip(pb, 4); //CRC + c->ver = avio_r8(pb); + if(c->ver != 8){ + av_log(s, AV_LOG_ERROR, "Unknown stream version %d\n", c->ver); + return AVERROR_PATCHWELCOME; + } + c->samples = ffio_read_varlen(pb); + ffio_read_varlen(pb); //silence samples at the beginning + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MUSEPACK8; + st->codec->bits_per_coded_sample = 16; + + st->codec->extradata_size = 2; + st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + avio_read(pb, st->codec->extradata, st->codec->extradata_size); + + st->codec->channels = (st->codec->extradata[1] >> 4) + 1; + st->codec->sample_rate = mpc8_rate[st->codec->extradata[0] >> 5]; + avpriv_set_pts_info(st, 32, 1152 << (st->codec->extradata[1]&3)*2, st->codec->sample_rate); + st->start_time = 0; + st->duration = c->samples / (1152 << (st->codec->extradata[1]&3)*2); + size -= avio_tell(pb) - pos; + if (size > 0) + avio_skip(pb, size); + + if (pb->seekable) { + int64_t pos = avio_tell(s->pb); + c->apetag_start = ff_ape_parse_tag(s); + avio_seek(s->pb, pos, SEEK_SET); + } + + return 0; +} + +static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPCContext *c = s->priv_data; + int tag; + int64_t pos, size; + + while(!url_feof(s->pb)){ + pos = avio_tell(s->pb); + + /* don't return bogus packets with the ape tag data */ + if (c->apetag_start && pos >= c->apetag_start) + return AVERROR_EOF; + + mpc8_get_chunk_header(s->pb, &tag, &size); + if (size < 0) + return -1; + if(tag == TAG_AUDIOPACKET){ + if(av_get_packet(s->pb, pkt, size) < 0) + return AVERROR(ENOMEM); + pkt->stream_index = 0; + pkt->duration = 1; + return 0; + } + if(tag == TAG_STREAMEND) + return AVERROR(EIO); + mpc8_handle_chunk(s, tag, pos, size); + } + return AVERROR_EOF; +} + +static int mpc8_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + AVStream *st = s->streams[stream_index]; + int index = av_index_search_timestamp(st, timestamp, flags); + + if(index < 0) return -1; + if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) + return -1; + ff_update_cur_dts(s, st, st->index_entries[index].timestamp); + return 0; +} + + +AVInputFormat ff_mpc8_demuxer = { + .name = "mpc8", + .long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), + .priv_data_size = sizeof(MPCContext), + .read_probe = mpc8_probe, + .read_header = mpc8_read_header, + .read_packet = mpc8_read_packet, + .read_seek = mpc8_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc8.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpc8.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mpc8.o libavformat/mpc8.o: libavformat/mpc8.c \ + libavcodec/get_bits.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h libavcodec/unary.h \ + libavcodec/get_bits.h libavformat/apetag.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpc8.o Binary file ffmpeg/libavformat/mpc8.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpeg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpeg.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,917 @@ +/* + * MPEG1/2 demuxer + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "mpeg.h" + +#if CONFIG_VOBSUB_DEMUXER +# include "subtitles.h" +# include "libavutil/bprint.h" +#endif + +#undef NDEBUG +#include +#include "libavutil/avassert.h" + +/*********************************************/ +/* demux code */ + +#define MAX_SYNC_SIZE 100000 + +static int check_pes(const uint8_t *p, const uint8_t *end){ + int pes1; + int pes2= (p[3] & 0xC0) == 0x80 + && (p[4] & 0xC0) != 0x40 + &&((p[4] & 0xC0) == 0x00 || (p[4]&0xC0)>>2 == (p[6]&0xF0)); + + for(p+=3; pbuf_size; i++){ + code = (code<<8) + p->buf[i]; + if ((code & 0xffffff00) == 0x100) { + int len= p->buf[i+1] << 8 | p->buf[i+2]; + int pes= check_pes(p->buf+i, p->buf+p->buf_size); + int pack = check_pack_header(p->buf+i); + + if(code == SYSTEM_HEADER_START_CODE) sys++; + else if(code == PACK_START_CODE && pack) pspack++; + else if((code & 0xf0) == VIDEO_ID && pes) vid++; + // skip pes payload to avoid start code emulation for private + // and audio streams + else if((code & 0xe0) == AUDIO_ID && pes) {audio++; i+=len;} + else if(code == PRIVATE_STREAM_1 && pes) {priv1++; i+=len;} + else if(code == 0x1fd && pes) vid++; //VC1 + + else if((code & 0xf0) == VIDEO_ID && !pes) invalid++; + else if((code & 0xe0) == AUDIO_ID && !pes) invalid++; + else if(code == PRIVATE_STREAM_1 && !pes) invalid++; + } + } + + if(vid+audio > invalid+1) /* invalid VDR files nd short PES streams */ + score= AVPROBE_SCORE_MAX/4; + + if(sys>invalid && sys*9 <= pspack*10) + return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + if(pspack > invalid && (priv1+vid+audio)*10 >= pspack*9) + return pspack > 2 ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + if((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys && !pspack && p->buf_size>2048 && vid + audio > invalid) /* PES stream */ + return (audio > 12 || vid > 3 + 2*invalid) ? AVPROBE_SCORE_MAX/2+2 : AVPROBE_SCORE_MAX/4; + + //02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1 + //mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6 + //Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618 + return score; +} + + +typedef struct MpegDemuxContext { + int32_t header_state; + unsigned char psm_es_type[256]; + int sofdec; + int dvd; + int imkh_cctv; +#if CONFIG_VOBSUB_DEMUXER + AVFormatContext *sub_ctx; + FFDemuxSubtitlesQueue q; +#endif +} MpegDemuxContext; + +static int mpegps_read_header(AVFormatContext *s) +{ + MpegDemuxContext *m = s->priv_data; + char buffer[7]; + int64_t last_pos = avio_tell(s->pb); + + m->header_state = 0xff; + s->ctx_flags |= AVFMTCTX_NOHEADER; + + avio_get_str(s->pb, 6, buffer, sizeof(buffer)); + if (!memcmp("IMKH", buffer, 4)) { + m->imkh_cctv = 1; + } else if (!memcmp("Sofdec", buffer, 6)) { + m->sofdec = 1; + } else + avio_seek(s->pb, last_pos, SEEK_SET); + + /* no need to do more */ + return 0; +} + +static int64_t get_pts(AVIOContext *pb, int c) +{ + uint8_t buf[5]; + + buf[0] = c<0 ? avio_r8(pb) : c; + avio_read(pb, buf+1, 4); + + return ff_parse_pes_pts(buf); +} + +static int find_next_start_code(AVIOContext *pb, int *size_ptr, + int32_t *header_state) +{ + unsigned int state, v; + int val, n; + + state = *header_state; + n = *size_ptr; + while (n > 0) { + if (url_feof(pb)) + break; + v = avio_r8(pb); + n--; + if (state == 0x000001) { + state = ((state << 8) | v) & 0xffffff; + val = state; + goto found; + } + state = ((state << 8) | v) & 0xffffff; + } + val = -1; + found: + *header_state = state; + *size_ptr = n; + return val; +} + +/** + * Extract stream types from a program stream map + * According to ISO/IEC 13818-1 ('MPEG-2 Systems') table 2-35 + * + * @return number of bytes occupied by PSM in the bitstream + */ +static long mpegps_psm_parse(MpegDemuxContext *m, AVIOContext *pb) +{ + int psm_length, ps_info_length, es_map_length; + + psm_length = avio_rb16(pb); + avio_r8(pb); + avio_r8(pb); + ps_info_length = avio_rb16(pb); + + /* skip program_stream_info */ + avio_skip(pb, ps_info_length); + es_map_length = avio_rb16(pb); + + /* at least one es available? */ + while (es_map_length >= 4){ + unsigned char type = avio_r8(pb); + unsigned char es_id = avio_r8(pb); + uint16_t es_info_length = avio_rb16(pb); + /* remember mapping from stream id to stream type */ + m->psm_es_type[es_id] = type; + /* skip program_stream_info */ + avio_skip(pb, es_info_length); + es_map_length -= 4 + es_info_length; + } + avio_rb32(pb); /* crc32 */ + return 2 + psm_length; +} + +/* read the next PES header. Return its position in ppos + (if not NULL), and its start code, pts and dts. + */ +static int mpegps_read_pes_header(AVFormatContext *s, + int64_t *ppos, int *pstart_code, + int64_t *ppts, int64_t *pdts) +{ + MpegDemuxContext *m = s->priv_data; + int len, size, startcode, c, flags, header_len; + int pes_ext, ext2_len, id_ext, skip; + int64_t pts, dts; + int64_t last_sync= avio_tell(s->pb); + + error_redo: + avio_seek(s->pb, last_sync, SEEK_SET); + redo: + /* next start code (should be immediately after) */ + m->header_state = 0xff; + size = MAX_SYNC_SIZE; + startcode = find_next_start_code(s->pb, &size, &m->header_state); + last_sync = avio_tell(s->pb); + if (startcode < 0){ + if(url_feof(s->pb)) + return AVERROR_EOF; + //FIXME we should remember header_state + return AVERROR(EAGAIN); + } + + if (startcode == PACK_START_CODE) + goto redo; + if (startcode == SYSTEM_HEADER_START_CODE) + goto redo; + if (startcode == PADDING_STREAM) { + avio_skip(s->pb, avio_rb16(s->pb)); + goto redo; + } + if (startcode == PRIVATE_STREAM_2) { + if (!m->sofdec) { + /* Need to detect whether this from a DVD or a 'Sofdec' stream */ + int len = avio_rb16(s->pb); + int bytesread = 0; + uint8_t *ps2buf = av_malloc(len); + + if (ps2buf) { + bytesread = avio_read(s->pb, ps2buf, len); + + if (bytesread != len) { + avio_skip(s->pb, len - bytesread); + } else { + uint8_t *p = 0; + if (len >= 6) + p = memchr(ps2buf, 'S', len - 5); + + if (p) + m->sofdec = !memcmp(p+1, "ofdec", 5); + + m->sofdec -= !m->sofdec; + + if (m->sofdec < 0) { + if (len == 980 && ps2buf[0] == 0) { + /* PCI structure? */ + uint32_t startpts = AV_RB32(ps2buf + 0x0d); + uint32_t endpts = AV_RB32(ps2buf + 0x11); + uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f); + uint8_t mins = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f); + uint8_t secs = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f); + + m->dvd = (hours <= 23 && + mins <= 59 && + secs <= 59 && + (ps2buf[0x19] & 0x0f) < 10 && + (ps2buf[0x1a] & 0x0f) < 10 && + (ps2buf[0x1b] & 0x0f) < 10 && + endpts >= startpts); + } else if (len == 1018 && ps2buf[0] == 1) { + /* DSI structure? */ + uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f); + uint8_t mins = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f); + uint8_t secs = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f); + + m->dvd = (hours <= 23 && + mins <= 59 && + secs <= 59 && + (ps2buf[0x1d] & 0x0f) < 10 && + (ps2buf[0x1e] & 0x0f) < 10 && + (ps2buf[0x1f] & 0x0f) < 10); + } + } + } + + av_free(ps2buf); + + /* If this isn't a DVD packet or no memory + * could be allocated, just ignore it. + * If we did, move back to the start of the + * packet (plus 'length' field) */ + if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) { + /* Skip back failed. + * This packet will be lost but that can't be helped + * if we can't skip back + */ + goto redo; + } + } else { + /* No memory */ + avio_skip(s->pb, len); + goto redo; + } + } else if (!m->dvd) { + int len = avio_rb16(s->pb); + avio_skip(s->pb, len); + goto redo; + } + } + if (startcode == PROGRAM_STREAM_MAP) { + mpegps_psm_parse(m, s->pb); + goto redo; + } + + /* find matching stream */ + if (!((startcode >= 0x1c0 && startcode <= 0x1df) || + (startcode >= 0x1e0 && startcode <= 0x1ef) || + (startcode == 0x1bd) || + (startcode == PRIVATE_STREAM_2) || + (startcode == 0x1fd))) + goto redo; + if (ppos) { + *ppos = avio_tell(s->pb) - 4; + } + len = avio_rb16(s->pb); + pts = + dts = AV_NOPTS_VALUE; + if (startcode != PRIVATE_STREAM_2) + { + /* stuffing */ + for(;;) { + if (len < 1) + goto error_redo; + c = avio_r8(s->pb); + len--; + /* XXX: for mpeg1, should test only bit 7 */ + if (c != 0xff) + break; + } + if ((c & 0xc0) == 0x40) { + /* buffer scale & size */ + avio_r8(s->pb); + c = avio_r8(s->pb); + len -= 2; + } + if ((c & 0xe0) == 0x20) { + dts = pts = get_pts(s->pb, c); + len -= 4; + if (c & 0x10){ + dts = get_pts(s->pb, -1); + len -= 5; + } + } else if ((c & 0xc0) == 0x80) { + /* mpeg 2 PES */ + flags = avio_r8(s->pb); + header_len = avio_r8(s->pb); + len -= 2; + if (header_len > len) + goto error_redo; + len -= header_len; + if (flags & 0x80) { + dts = pts = get_pts(s->pb, -1); + header_len -= 5; + if (flags & 0x40) { + dts = get_pts(s->pb, -1); + header_len -= 5; + } + } + if (flags & 0x3f && header_len == 0){ + flags &= 0xC0; + av_log(s, AV_LOG_WARNING, "Further flags set but no bytes left\n"); + } + if (flags & 0x01) { /* PES extension */ + pes_ext = avio_r8(s->pb); + header_len--; + /* Skip PES private data, program packet sequence counter and P-STD buffer */ + skip = (pes_ext >> 4) & 0xb; + skip += skip & 0x9; + if (pes_ext & 0x40 || skip > header_len){ + av_log(s, AV_LOG_WARNING, "pes_ext %X is invalid\n", pes_ext); + pes_ext=skip=0; + } + avio_skip(s->pb, skip); + header_len -= skip; + + if (pes_ext & 0x01) { /* PES extension 2 */ + ext2_len = avio_r8(s->pb); + header_len--; + if ((ext2_len & 0x7f) > 0) { + id_ext = avio_r8(s->pb); + if ((id_ext & 0x80) == 0) + startcode = ((startcode & 0xff) << 8) | id_ext; + header_len--; + } + } + } + if(header_len < 0) + goto error_redo; + avio_skip(s->pb, header_len); + } + else if( c!= 0xf ) + goto redo; + } + + if (startcode == PRIVATE_STREAM_1) { + startcode = avio_r8(s->pb); + len--; + } + if(len<0) + goto error_redo; + if(dts != AV_NOPTS_VALUE && ppos){ + int i; + for(i=0; inb_streams; i++){ + if(startcode == s->streams[i]->id && + s->pb->seekable /* index useless on streams anyway */) { + ff_reduce_index(s, i); + av_add_index_entry(s->streams[i], *ppos, dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + } + } + } + + *pstart_code = startcode; + *ppts = pts; + *pdts = dts; + return len; +} + +static int mpegps_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegDemuxContext *m = s->priv_data; + AVStream *st; + int len, startcode, i, es_type, ret; + int lpcm_header_len = -1; //Init to supress warning + int request_probe= 0; + enum AVCodecID codec_id = AV_CODEC_ID_NONE; + enum AVMediaType type; + int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work + + redo: + len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts); + if (len < 0) + return len; + + if (startcode >= 0x80 && startcode <= 0xcf) { + if(len < 4) + goto skip; + + /* audio: skip header */ + avio_r8(s->pb); + lpcm_header_len = avio_rb16(s->pb); + len -= 3; + if (startcode >= 0xb0 && startcode <= 0xbf) { + /* MLP/TrueHD audio has a 4-byte header */ + avio_r8(s->pb); + len--; + } + } + + /* now find stream */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + if (st->id == startcode) + goto found; + } + + es_type = m->psm_es_type[startcode & 0xff]; + if(es_type == STREAM_TYPE_VIDEO_MPEG1){ + codec_id = AV_CODEC_ID_MPEG2VIDEO; + type = AVMEDIA_TYPE_VIDEO; + } else if(es_type == STREAM_TYPE_VIDEO_MPEG2){ + codec_id = AV_CODEC_ID_MPEG2VIDEO; + type = AVMEDIA_TYPE_VIDEO; + } else if(es_type == STREAM_TYPE_AUDIO_MPEG1 || + es_type == STREAM_TYPE_AUDIO_MPEG2){ + codec_id = AV_CODEC_ID_MP3; + type = AVMEDIA_TYPE_AUDIO; + } else if(es_type == STREAM_TYPE_AUDIO_AAC){ + codec_id = AV_CODEC_ID_AAC; + type = AVMEDIA_TYPE_AUDIO; + } else if(es_type == STREAM_TYPE_VIDEO_MPEG4){ + codec_id = AV_CODEC_ID_MPEG4; + type = AVMEDIA_TYPE_VIDEO; + } else if(es_type == STREAM_TYPE_VIDEO_H264){ + codec_id = AV_CODEC_ID_H264; + type = AVMEDIA_TYPE_VIDEO; + } else if(es_type == STREAM_TYPE_AUDIO_AC3){ + codec_id = AV_CODEC_ID_AC3; + type = AVMEDIA_TYPE_AUDIO; + } else if(m->imkh_cctv && es_type == 0x91){ + codec_id = AV_CODEC_ID_PCM_MULAW; + type = AVMEDIA_TYPE_AUDIO; + } else if (startcode >= 0x1e0 && startcode <= 0x1ef) { + static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 }; + unsigned char buf[8]; + avio_read(s->pb, buf, 8); + avio_seek(s->pb, -8, SEEK_CUR); + if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1)) + codec_id = AV_CODEC_ID_CAVS; + else + request_probe= 1; + type = AVMEDIA_TYPE_VIDEO; + } else if (startcode == PRIVATE_STREAM_2) { + type = AVMEDIA_TYPE_DATA; + codec_id = AV_CODEC_ID_DVD_NAV; + } else if (startcode >= 0x1c0 && startcode <= 0x1df) { + type = AVMEDIA_TYPE_AUDIO; + codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2; + } else if (startcode >= 0x80 && startcode <= 0x87) { + type = AVMEDIA_TYPE_AUDIO; + codec_id = AV_CODEC_ID_AC3; + } else if ( ( startcode >= 0x88 && startcode <= 0x8f) + ||( startcode >= 0x98 && startcode <= 0x9f)) { + /* 0x90 - 0x97 is reserved for SDDS in DVD specs */ + type = AVMEDIA_TYPE_AUDIO; + codec_id = AV_CODEC_ID_DTS; + } else if (startcode >= 0xa0 && startcode <= 0xaf) { + type = AVMEDIA_TYPE_AUDIO; + if(lpcm_header_len == 6) { + codec_id = AV_CODEC_ID_MLP; + } else { + /* 16 bit form will be handled as AV_CODEC_ID_PCM_S16BE */ + codec_id = AV_CODEC_ID_PCM_DVD; + } + } else if (startcode >= 0xb0 && startcode <= 0xbf) { + type = AVMEDIA_TYPE_AUDIO; + codec_id = AV_CODEC_ID_TRUEHD; + } else if (startcode >= 0xc0 && startcode <= 0xcf) { + /* Used for both AC-3 and E-AC-3 in EVOB files */ + type = AVMEDIA_TYPE_AUDIO; + codec_id = AV_CODEC_ID_AC3; + } else if (startcode >= 0x20 && startcode <= 0x3f) { + type = AVMEDIA_TYPE_SUBTITLE; + codec_id = AV_CODEC_ID_DVD_SUBTITLE; + } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { + type = AVMEDIA_TYPE_VIDEO; + codec_id = AV_CODEC_ID_VC1; + } else { + skip: + /* skip packet */ + avio_skip(s->pb, len); + goto redo; + } + /* no stream found: add a new stream */ + st = avformat_new_stream(s, NULL); + if (!st) + goto skip; + st->id = startcode; + st->codec->codec_type = type; + st->codec->codec_id = codec_id; + if (st->codec->codec_id == AV_CODEC_ID_PCM_MULAW) { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 8000; + } + st->request_probe = request_probe; + if (codec_id != AV_CODEC_ID_PCM_S16BE) + st->need_parsing = AVSTREAM_PARSE_FULL; + found: + if(st->discard >= AVDISCARD_ALL) + goto skip; + if (startcode >= 0xa0 && startcode <= 0xaf) { + if (lpcm_header_len == 6 && st->codec->codec_id == AV_CODEC_ID_MLP) { + if (len < 6) + goto skip; + avio_skip(s->pb, 6); + len -=6; + } else { + int b1, freq; + + /* for LPCM, we just skip the header and consider it is raw + audio data */ + if (len <= 3) + goto skip; + avio_r8(s->pb); /* emphasis (1), muse(1), reserved(1), frame number(5) */ + b1 = avio_r8(s->pb); /* quant (2), freq(2), reserved(1), channels(3) */ + avio_r8(s->pb); /* dynamic range control (0x80 = off) */ + len -= 3; + freq = (b1 >> 4) & 3; + st->codec->sample_rate = lpcm_freq_tab[freq]; + st->codec->channels = 1 + (b1 & 7); + st->codec->bits_per_coded_sample = 16 + ((b1 >> 6) & 3) * 4; + st->codec->bit_rate = st->codec->channels * + st->codec->sample_rate * + st->codec->bits_per_coded_sample; + if (st->codec->bits_per_coded_sample == 16) + st->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + else if (st->codec->bits_per_coded_sample == 28) + return AVERROR(EINVAL); + } + } + ret = av_get_packet(s->pb, pkt, len); + pkt->pts = pts; + pkt->dts = dts; + pkt->pos = dummy_pos; + pkt->stream_index = st->index; + av_dlog(s, "%d: pts=%0.3f dts=%0.3f size=%d\n", + pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0, + pkt->size); + + return (ret < 0) ? ret : 0; +} + +static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + int len, startcode; + int64_t pos, pts, dts; + + pos = *ppos; + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + for(;;) { + len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts); + if (len < 0) { + av_dlog(s, "none (ret=%d)\n", len); + return AV_NOPTS_VALUE; + } + if (startcode == s->streams[stream_index]->id && + dts != AV_NOPTS_VALUE) { + break; + } + avio_skip(s->pb, len); + } + av_dlog(s, "pos=0x%"PRIx64" dts=0x%"PRIx64" %0.3f\n", + pos, dts, dts / 90000.0); + *ppos = pos; + return dts; +} + +AVInputFormat ff_mpegps_demuxer = { + .name = "mpeg", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-PS (MPEG-2 Program Stream)"), + .priv_data_size = sizeof(MpegDemuxContext), + .read_probe = mpegps_probe, + .read_header = mpegps_read_header, + .read_packet = mpegps_read_packet, + .read_timestamp = mpegps_read_dts, + .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, +}; + +#if CONFIG_VOBSUB_DEMUXER + +#define REF_STRING "# VobSub index file," + +static int vobsub_probe(AVProbeData *p) +{ + if (!strncmp(p->buf, REF_STRING, sizeof(REF_STRING) - 1)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int vobsub_read_header(AVFormatContext *s) +{ + int i, ret = 0, header_parsed = 0, langidx = 0; + MpegDemuxContext *vobsub = s->priv_data; + char *sub_name = NULL; + size_t fname_len; + char *ext, *header_str; + AVBPrint header; + int64_t delay = 0; + AVStream *st = NULL; + + sub_name = av_strdup(s->filename); + fname_len = strlen(sub_name); + ext = sub_name - 3 + fname_len; + if (fname_len < 4 || *(ext - 1) != '.') { + av_log(s, AV_LOG_ERROR, "The input index filename is too short " + "to guess the associated .SUB file\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + memcpy(ext, !strncmp(ext, "IDX", 3) ? "SUB" : "sub", 3); + av_log(s, AV_LOG_VERBOSE, "IDX/SUB: %s -> %s\n", s->filename, sub_name); + ret = avformat_open_input(&vobsub->sub_ctx, sub_name, &ff_mpegps_demuxer, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to open %s as MPEG subtitles\n", sub_name); + goto end; + } + + av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); + while (!url_feof(s->pb)) { + char line[2048]; + int len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!strncmp(line, "id:", 3)) { + int n, stream_id = 0; + char id[64] = {0}; + + n = sscanf(line, "id: %63[^,], index: %u", id, &stream_id); + if (n != 2) { + av_log(s, AV_LOG_WARNING, "Unable to parse index line '%s', " + "assuming 'id: und, index: 0'\n", line); + strcpy(id, "und"); + stream_id = 0; + } + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto end; + } + st->id = stream_id; + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_DVD_SUBTITLE; + av_dict_set(&st->metadata, "language", id, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] id=%s\n", stream_id, id); + header_parsed = 1; + + } else if (st && !strncmp(line, "timestamp:", 10)) { + AVPacket *sub; + int hh, mm, ss, ms; + int64_t pos, timestamp; + const char *p = line + 10; + + if (sscanf(p, "%02d:%02d:%02d:%03d, filepos: %"SCNx64, + &hh, &mm, &ss, &ms, &pos) != 5) { + av_log(s, AV_LOG_ERROR, "Unable to parse timestamp line '%s', " + "abort parsing\n", line); + break; + } + timestamp = (hh*3600LL + mm*60LL + ss) * 1000LL + ms + delay; + timestamp = av_rescale_q(timestamp, (AVRational){1,1000}, st->time_base); + + sub = ff_subtitles_queue_insert(&vobsub->q, "", 0, 0); + if (!sub) { + ret = AVERROR(ENOMEM); + goto end; + } + sub->pos = pos; + sub->pts = timestamp; + sub->stream_index = s->nb_streams - 1; + + } else if (st && !strncmp(line, "alt:", 4)) { + const char *p = line + 4; + + while (*p == ' ') + p++; + av_dict_set(&st->metadata, "title", p, 0); + av_log(s, AV_LOG_DEBUG, "IDX stream[%d] name=%s\n", st->id, p); + header_parsed = 1; + + } else if (!strncmp(line, "delay:", 6)) { + int sign = 1, hh = 0, mm = 0, ss = 0, ms = 0; + const char *p = line + 6; + + while (*p == ' ') + p++; + if (*p == '-' || *p == '+') { + sign = *p == '-' ? -1 : 1; + p++; + } + sscanf(p, "%d:%d:%d:%d", &hh, &mm, &ss, &ms); + delay = ((hh*3600LL + mm*60LL + ss) * 1000LL + ms) * sign; + + } else if (!strncmp(line, "langidx:", 8)) { + const char *p = line + 8; + + if (sscanf(p, "%d", &langidx) != 1) + av_log(s, AV_LOG_ERROR, "Invalid langidx specified\n"); + + } else if (!header_parsed) { + if (line[0] && line[0] != '#') + av_bprintf(&header, "%s\n", line); + } + } + + if (langidx < s->nb_streams) + s->streams[langidx]->disposition |= AV_DISPOSITION_DEFAULT; + + ff_subtitles_queue_finalize(&vobsub->q); + + if (!av_bprint_is_complete(&header)) { + av_bprint_finalize(&header, NULL); + ret = AVERROR(ENOMEM); + goto end; + } + av_bprint_finalize(&header, &header_str); + for (i = 0; i < s->nb_streams; i++) { + AVStream *sub_st = s->streams[i]; + sub_st->codec->extradata = av_strdup(header_str); + sub_st->codec->extradata_size = header.len; + } + av_free(header_str); + +end: + av_free(sub_name); + return ret; +} + +#define FAIL(r) do { ret = r; goto fail; } while (0) + +static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MpegDemuxContext *vobsub = s->priv_data; + FFDemuxSubtitlesQueue *q = &vobsub->q; + AVIOContext *pb = vobsub->sub_ctx->pb; + int ret, psize, len16 = -1; + AVPacket idx_pkt; + + ret = ff_subtitles_queue_read_packet(q, &idx_pkt); + if (ret < 0) + return ret; + + /* compute maximum packet size using the next packet position. This is + * useful when the len in the header is non-sense */ + if (q->current_sub_idx < q->nb_subs) { + psize = q->subs[q->current_sub_idx].pos - idx_pkt.pos; + } else { + int64_t fsize = avio_size(pb); + psize = fsize < 0 ? 0xffff : fsize - idx_pkt.pos; + } + + avio_seek(pb, idx_pkt.pos, SEEK_SET); + + av_init_packet(pkt); + pkt->size = 0; + pkt->data = NULL; + + do { + int n, to_read, startcode; + int64_t pts, dts; + + ret = mpegps_read_pes_header(vobsub->sub_ctx, NULL, &startcode, &pts, &dts); + if (ret < 0) + FAIL(ret); + to_read = ret & 0xffff; + + /* this prevents reads above the current packet */ + if (pkt->size + to_read > psize) + break; + + /* if the len is computed, we check for overread */ + if (len16 != -1 && pkt->size + to_read > len16) + break; + + /* the current chunk doesn't match the stream index (unlikely) */ + if ((startcode & 0x1f) != idx_pkt.stream_index) + break; + + ret = av_grow_packet(pkt, to_read); + if (ret < 0) + FAIL(ret); + + n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read); + if (n < to_read) + pkt->size -= to_read - n; + + /* first chunk contains the total len of the packet to raise */ + if (len16 == -1 && n > 2) + len16 = AV_RB16(pkt->data); + } while (len16 != -1 && pkt->size != len16); + + pkt->pts = pkt->dts = idx_pkt.pts; + pkt->pos = idx_pkt.pos; + pkt->stream_index = idx_pkt.stream_index; + + av_free_packet(&idx_pkt); + return 0; + +fail: + av_free_packet(&idx_pkt); + return ret; +} + +static int vobsub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MpegDemuxContext *vobsub = s->priv_data; + return ff_subtitles_queue_seek(&vobsub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int vobsub_read_close(AVFormatContext *s) +{ + MpegDemuxContext *vobsub = s->priv_data; + ff_subtitles_queue_clean(&vobsub->q); + if (vobsub->sub_ctx) + avformat_close_input(&vobsub->sub_ctx); + return 0; +} + +AVInputFormat ff_vobsub_demuxer = { + .name = "vobsub", + .long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"), + .priv_data_size = sizeof(MpegDemuxContext), + .read_probe = vobsub_probe, + .read_header = vobsub_read_header, + .read_packet = vobsub_read_packet, + .read_seek2 = vobsub_read_seek, + .read_close = vobsub_read_close, + .flags = AVFMT_SHOW_IDS, + .extensions = "idx", +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpeg.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpeg.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mpeg.o libavformat/mpeg.o: libavformat/mpeg.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/mpeg.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h \ + libavutil/avassert.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpeg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpeg.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,73 @@ +/* + * MPEG1/2 muxer and demuxer common defines + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_MPEG_H +#define AVFORMAT_MPEG_H + +#include +#include "libavutil/intreadwrite.h" + +#define PACK_START_CODE ((unsigned int)0x000001ba) +#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb) +#define SEQUENCE_END_CODE ((unsigned int)0x000001b7) +#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00) +#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100) +#define ISO_11172_END_CODE ((unsigned int)0x000001b9) + +/* mpeg2 */ +#define PROGRAM_STREAM_MAP 0x1bc +#define PRIVATE_STREAM_1 0x1bd +#define PADDING_STREAM 0x1be +#define PRIVATE_STREAM_2 0x1bf + +#define AUDIO_ID 0xc0 +#define VIDEO_ID 0xe0 +#define AC3_ID 0x80 +#define DTS_ID 0x8a +#define LPCM_ID 0xa0 +#define SUB_ID 0x20 + +#define STREAM_TYPE_VIDEO_MPEG1 0x01 +#define STREAM_TYPE_VIDEO_MPEG2 0x02 +#define STREAM_TYPE_AUDIO_MPEG1 0x03 +#define STREAM_TYPE_AUDIO_MPEG2 0x04 +#define STREAM_TYPE_PRIVATE_SECTION 0x05 +#define STREAM_TYPE_PRIVATE_DATA 0x06 +#define STREAM_TYPE_AUDIO_AAC 0x0f +#define STREAM_TYPE_VIDEO_MPEG4 0x10 +#define STREAM_TYPE_VIDEO_H264 0x1b +#define STREAM_TYPE_VIDEO_CAVS 0x42 + +#define STREAM_TYPE_AUDIO_AC3 0x81 +#define STREAM_TYPE_AUDIO_DTS 0x8a + +static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 }; + +/** + * Parse MPEG-PES five-byte timestamp + */ +static inline int64_t ff_parse_pes_pts(const uint8_t *buf) { + return (int64_t)(*buf & 0x0e) << 29 | + (AV_RB16(buf+1) >> 1) << 15 | + AV_RB16(buf+3) >> 1; +} + +#endif /* AVFORMAT_MPEG_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpeg.o Binary file ffmpeg/libavformat/mpeg.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1242 @@ +/* + * MPEG1/2 muxer + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/fifo.h" +#include "libavutil/log.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavcodec/put_bits.h" +#include "avformat.h" +#include "internal.h" +#include "mpeg.h" + +#define MAX_PAYLOAD_SIZE 4096 + +#undef NDEBUG +#include + +typedef struct PacketDesc { + int64_t pts; + int64_t dts; + int size; + int unwritten_size; + int flags; + struct PacketDesc *next; +} PacketDesc; + +typedef struct { + AVFifoBuffer *fifo; + uint8_t id; + int max_buffer_size; /* in bytes */ + int buffer_index; + PacketDesc *predecode_packet; + PacketDesc *premux_packet; + PacketDesc **next_packet; + int packet_number; + uint8_t lpcm_header[3]; + int lpcm_align; + int bytes_to_iframe; + int align_iframe; + int64_t vobu_start_pts; +} StreamInfo; + +typedef struct { + const AVClass *class; + int packet_size; /* required packet size */ + int packet_number; + int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ + int system_header_freq; + int system_header_size; + int user_mux_rate; /* bitrate in units of bits/s */ + int mux_rate; /* bitrate in units of 50 bytes/s */ + /* stream info */ + int audio_bound; + int video_bound; + int is_mpeg2; + int is_vcd; + int is_svcd; + int is_dvd; + int64_t last_scr; /* current system clock */ + + double vcd_padding_bitrate; //FIXME floats + int64_t vcd_padding_bytes_written; + + int preload; +} MpegMuxContext; + +extern AVOutputFormat ff_mpeg1vcd_muxer; +extern AVOutputFormat ff_mpeg2dvd_muxer; +extern AVOutputFormat ff_mpeg2svcd_muxer; +extern AVOutputFormat ff_mpeg2vob_muxer; + +static int put_pack_header(AVFormatContext *ctx, + uint8_t *buf, int64_t timestamp) +{ + MpegMuxContext *s = ctx->priv_data; + PutBitContext pb; + + init_put_bits(&pb, buf, 128); + + put_bits32(&pb, PACK_START_CODE); + if (s->is_mpeg2) { + put_bits(&pb, 2, 0x1); + } else { + put_bits(&pb, 4, 0x2); + } + put_bits(&pb, 3, (uint32_t)((timestamp >> 30) & 0x07)); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (uint32_t)((timestamp >> 15) & 0x7fff)); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (uint32_t)((timestamp ) & 0x7fff)); + put_bits(&pb, 1, 1); + if (s->is_mpeg2) { + /* clock extension */ + put_bits(&pb, 9, 0); + } + put_bits(&pb, 1, 1); + put_bits(&pb, 22, s->mux_rate); + put_bits(&pb, 1, 1); + if (s->is_mpeg2) { + put_bits(&pb, 1, 1); + put_bits(&pb, 5, 0x1f); /* reserved */ + put_bits(&pb, 3, 0); /* stuffing length */ + } + flush_put_bits(&pb); + return put_bits_ptr(&pb) - pb.buf; +} + +static int put_system_header(AVFormatContext *ctx, uint8_t *buf,int only_for_stream_id) +{ + MpegMuxContext *s = ctx->priv_data; + int size, i, private_stream_coded, id; + PutBitContext pb; + + init_put_bits(&pb, buf, 128); + + put_bits32(&pb, SYSTEM_HEADER_START_CODE); + put_bits(&pb, 16, 0); + put_bits(&pb, 1, 1); + + put_bits(&pb, 22, s->mux_rate); /* maximum bit rate of the multiplexed stream */ + put_bits(&pb, 1, 1); /* marker */ + if (s->is_vcd && only_for_stream_id==VIDEO_ID) { + /* This header applies only to the video stream (see VCD standard p. IV-7)*/ + put_bits(&pb, 6, 0); + } else + put_bits(&pb, 6, s->audio_bound); + + if (s->is_vcd) { + /* see VCD standard, p. IV-7*/ + put_bits(&pb, 1, 0); + put_bits(&pb, 1, 1); + } else { + put_bits(&pb, 1, 0); /* variable bitrate*/ + put_bits(&pb, 1, 0); /* non constrainted bit stream */ + } + + if (s->is_vcd || s->is_dvd) { + /* see VCD standard p IV-7 */ + put_bits(&pb, 1, 1); /* audio locked */ + put_bits(&pb, 1, 1); /* video locked */ + } else { + put_bits(&pb, 1, 0); /* audio locked */ + put_bits(&pb, 1, 0); /* video locked */ + } + + put_bits(&pb, 1, 1); /* marker */ + + if (s->is_vcd && (only_for_stream_id & 0xe0) == AUDIO_ID) { + /* This header applies only to the audio stream (see VCD standard p. IV-7)*/ + put_bits(&pb, 5, 0); + } else + put_bits(&pb, 5, s->video_bound); + + if (s->is_dvd) { + put_bits(&pb, 1, 0); /* packet_rate_restriction_flag */ + put_bits(&pb, 7, 0x7f); /* reserved byte */ + } else + put_bits(&pb, 8, 0xff); /* reserved byte */ + + /* DVD-Video Stream_bound entries + id (0xB9) video, maximum P-STD for stream 0xE0. (P-STD_buffer_bound_scale = 1) + id (0xB8) audio, maximum P-STD for any MPEG audio (0xC0 to 0xC7) streams. If there are none set to 4096 (32x128). (P-STD_buffer_bound_scale = 0) + id (0xBD) private stream 1 (audio other than MPEG and subpictures). (P-STD_buffer_bound_scale = 1) + id (0xBF) private stream 2, NAV packs, set to 2x1024. */ + if (s->is_dvd) { + + int P_STD_max_video = 0; + int P_STD_max_mpeg_audio = 0; + int P_STD_max_mpeg_PS1 = 0; + + for(i=0;inb_streams;i++) { + StreamInfo *stream = ctx->streams[i]->priv_data; + + id = stream->id; + if (id == 0xbd && stream->max_buffer_size > P_STD_max_mpeg_PS1) { + P_STD_max_mpeg_PS1 = stream->max_buffer_size; + } else if (id >= 0xc0 && id <= 0xc7 && stream->max_buffer_size > P_STD_max_mpeg_audio) { + P_STD_max_mpeg_audio = stream->max_buffer_size; + } else if (id == 0xe0 && stream->max_buffer_size > P_STD_max_video) { + P_STD_max_video = stream->max_buffer_size; + } + } + + /* video */ + put_bits(&pb, 8, 0xb9); /* stream ID */ + put_bits(&pb, 2, 3); + put_bits(&pb, 1, 1); + put_bits(&pb, 13, P_STD_max_video / 1024); + + /* audio */ + if (P_STD_max_mpeg_audio == 0) + P_STD_max_mpeg_audio = 4096; + put_bits(&pb, 8, 0xb8); /* stream ID */ + put_bits(&pb, 2, 3); + put_bits(&pb, 1, 0); + put_bits(&pb, 13, P_STD_max_mpeg_audio / 128); + + /* private stream 1 */ + put_bits(&pb, 8, 0xbd); /* stream ID */ + put_bits(&pb, 2, 3); + put_bits(&pb, 1, 0); + put_bits(&pb, 13, P_STD_max_mpeg_PS1 / 128); + + /* private stream 2 */ + put_bits(&pb, 8, 0xbf); /* stream ID */ + put_bits(&pb, 2, 3); + put_bits(&pb, 1, 1); + put_bits(&pb, 13, 2); + } + else { + /* audio stream info */ + private_stream_coded = 0; + for(i=0;inb_streams;i++) { + StreamInfo *stream = ctx->streams[i]->priv_data; + + + /* For VCDs, only include the stream info for the stream + that the pack which contains this system belongs to. + (see VCD standard p. IV-7) */ + if ( !s->is_vcd || stream->id==only_for_stream_id + || only_for_stream_id==0) { + + id = stream->id; + if (id < 0xc0) { + /* special case for private streams (AC-3 uses that) */ + if (private_stream_coded) + continue; + private_stream_coded = 1; + id = 0xbd; + } + put_bits(&pb, 8, id); /* stream ID */ + put_bits(&pb, 2, 3); + if (id < 0xe0) { + /* audio */ + put_bits(&pb, 1, 0); + put_bits(&pb, 13, stream->max_buffer_size / 128); + } else { + /* video */ + put_bits(&pb, 1, 1); + put_bits(&pb, 13, stream->max_buffer_size / 1024); + } + } + } + } + + flush_put_bits(&pb); + size = put_bits_ptr(&pb) - pb.buf; + /* patch packet size */ + buf[4] = (size - 6) >> 8; + buf[5] = (size - 6) & 0xff; + + return size; +} + +static int get_system_header_size(AVFormatContext *ctx) +{ + int buf_index, i, private_stream_coded; + StreamInfo *stream; + MpegMuxContext *s = ctx->priv_data; + + if (s->is_dvd) + return 18; // DVD-Video system headers are 18 bytes fixed length. + + buf_index = 12; + private_stream_coded = 0; + for(i=0;inb_streams;i++) { + stream = ctx->streams[i]->priv_data; + if (stream->id < 0xc0) { + if (private_stream_coded) + continue; + private_stream_coded = 1; + } + buf_index += 3; + } + return buf_index; +} + +static int mpeg_mux_init(AVFormatContext *ctx) +{ + MpegMuxContext *s = ctx->priv_data; + int bitrate, i, mpa_id, mpv_id, mps_id, ac3_id, dts_id, lpcm_id, j; + AVStream *st; + StreamInfo *stream; + int audio_bitrate; + int video_bitrate; + + s->packet_number = 0; + s->is_vcd = (CONFIG_MPEG1VCD_MUXER && ctx->oformat == &ff_mpeg1vcd_muxer); + s->is_svcd = (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer); + s->is_mpeg2 = ((CONFIG_MPEG2VOB_MUXER && ctx->oformat == &ff_mpeg2vob_muxer) || + (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer) || + (CONFIG_MPEG2SVCD_MUXER && ctx->oformat == &ff_mpeg2svcd_muxer)); + s->is_dvd = (CONFIG_MPEG2DVD_MUXER && ctx->oformat == &ff_mpeg2dvd_muxer); + + if(ctx->packet_size) { + if (ctx->packet_size < 20 || ctx->packet_size > (1 << 23) + 10) { + av_log(ctx, AV_LOG_ERROR, "Invalid packet size %d\n", + ctx->packet_size); + goto fail; + } + s->packet_size = ctx->packet_size; + } else + s->packet_size = 2048; + if (ctx->max_delay < 0) /* Not set by the caller */ + ctx->max_delay = 0; + + s->vcd_padding_bytes_written = 0; + s->vcd_padding_bitrate=0; + + s->audio_bound = 0; + s->video_bound = 0; + mpa_id = AUDIO_ID; + ac3_id = AC3_ID; + dts_id = DTS_ID; + mpv_id = VIDEO_ID; + mps_id = SUB_ID; + lpcm_id = LPCM_ID; + for(i=0;inb_streams;i++) { + st = ctx->streams[i]; + stream = av_mallocz(sizeof(StreamInfo)); + if (!stream) + goto fail; + st->priv_data = stream; + + avpriv_set_pts_info(st, 64, 1, 90000); + + switch(st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if (st->codec->codec_id == AV_CODEC_ID_AC3) { + stream->id = ac3_id++; + } else if (st->codec->codec_id == AV_CODEC_ID_DTS) { + stream->id = dts_id++; + } else if (st->codec->codec_id == AV_CODEC_ID_PCM_S16BE) { + stream->id = lpcm_id++; + for(j = 0; j < 4; j++) { + if (lpcm_freq_tab[j] == st->codec->sample_rate) + break; + } + if (j == 4) + goto fail; + if (st->codec->channels > 8) + return -1; + stream->lpcm_header[0] = 0x0c; + stream->lpcm_header[1] = (st->codec->channels - 1) | (j << 4); + stream->lpcm_header[2] = 0x80; + stream->lpcm_align = st->codec->channels * 2; + } else { + stream->id = mpa_id++; + } + + /* This value HAS to be used for VCD (see VCD standard, p. IV-7). + Right now it is also used for everything else.*/ + stream->max_buffer_size = 4 * 1024; + s->audio_bound++; + break; + case AVMEDIA_TYPE_VIDEO: + stream->id = mpv_id++; + if (st->codec->rc_buffer_size) + stream->max_buffer_size = 6*1024 + st->codec->rc_buffer_size/8; + else { + av_log(ctx, AV_LOG_WARNING, "VBV buffer size not set, muxing may fail\n"); + stream->max_buffer_size = 230*1024; //FIXME this is probably too small as default + } + s->video_bound++; + break; + case AVMEDIA_TYPE_SUBTITLE: + stream->id = mps_id++; + stream->max_buffer_size = 16 * 1024; + break; + default: + return -1; + } + stream->fifo= av_fifo_alloc(16); + if (!stream->fifo) + goto fail; + } + bitrate = 0; + audio_bitrate = 0; + video_bitrate = 0; + for(i=0;inb_streams;i++) { + int codec_rate; + st = ctx->streams[i]; + stream = (StreamInfo*) st->priv_data; + + if(st->codec->rc_max_rate || stream->id==VIDEO_ID) + codec_rate= st->codec->rc_max_rate; + else + codec_rate= st->codec->bit_rate; + + if(!codec_rate) + codec_rate= (1<<21)*8*50/ctx->nb_streams; + + bitrate += codec_rate; + + if ((stream->id & 0xe0) == AUDIO_ID) + audio_bitrate += codec_rate; + else if (stream->id==VIDEO_ID) + video_bitrate += codec_rate; + } + + if (s->user_mux_rate) { + s->mux_rate = (s->user_mux_rate + (8 * 50) - 1) / (8 * 50); + } else { + /* we increase slightly the bitrate to take into account the + headers. XXX: compute it exactly */ + bitrate += bitrate / 20; + bitrate += 10000; + s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50); + } + + if (s->is_vcd) { + double overhead_rate; + + /* The VCD standard mandates that the mux_rate field is 3528 + (see standard p. IV-6). + The value is actually "wrong", i.e. if you calculate + it using the normal formula and the 75 sectors per second transfer + rate you get a different value because the real pack size is 2324, + not 2352. But the standard explicitly specifies that the mux_rate + field in the header must have this value.*/ +// s->mux_rate=2352 * 75 / 50; /* = 3528*/ + + /* The VCD standard states that the muxed stream must be + exactly 75 packs / second (the data rate of a single speed cdrom). + Since the video bitrate (probably 1150000 bits/sec) will be below + the theoretical maximum we have to add some padding packets + to make up for the lower data rate. + (cf. VCD standard p. IV-6 )*/ + + /* Add the header overhead to the data rate. + 2279 data bytes per audio pack, 2294 data bytes per video pack*/ + overhead_rate = ((audio_bitrate / 8.0) / 2279) * (2324 - 2279); + overhead_rate += ((video_bitrate / 8.0) / 2294) * (2324 - 2294); + overhead_rate *= 8; + + /* Add padding so that the full bitrate is 2324*75 bytes/sec */ + s->vcd_padding_bitrate = 2324 * 75 * 8 - (bitrate + overhead_rate); + } + + if (s->is_vcd || s->is_mpeg2) + /* every packet */ + s->pack_header_freq = 1; + else + /* every 2 seconds */ + s->pack_header_freq = 2 * bitrate / s->packet_size / 8; + + /* the above seems to make pack_header_freq zero sometimes */ + if (s->pack_header_freq == 0) + s->pack_header_freq = 1; + + if (s->is_mpeg2) + /* every 200 packets. Need to look at the spec. */ + s->system_header_freq = s->pack_header_freq * 40; + else if (s->is_vcd) + /* the standard mandates that there are only two system headers + in the whole file: one in the first packet of each stream. + (see standard p. IV-7 and IV-8) */ + s->system_header_freq = 0x7fffffff; + else + s->system_header_freq = s->pack_header_freq * 5; + + for(i=0;inb_streams;i++) { + stream = ctx->streams[i]->priv_data; + stream->packet_number = 0; + } + s->system_header_size = get_system_header_size(ctx); + s->last_scr = AV_NOPTS_VALUE; + return 0; + fail: + for(i=0;inb_streams;i++) { + av_free(ctx->streams[i]->priv_data); + } + return AVERROR(ENOMEM); +} + +static inline void put_timestamp(AVIOContext *pb, int id, int64_t timestamp) +{ + avio_w8(pb, + (id << 4) | + (((timestamp >> 30) & 0x07) << 1) | + 1); + avio_wb16(pb, (uint16_t)((((timestamp >> 15) & 0x7fff) << 1) | 1)); + avio_wb16(pb, (uint16_t)((((timestamp ) & 0x7fff) << 1) | 1)); +} + + +/* return the number of padding bytes that should be inserted into + the multiplexed stream.*/ +static int get_vcd_padding_size(AVFormatContext *ctx, int64_t pts) +{ + MpegMuxContext *s = ctx->priv_data; + int pad_bytes = 0; + + if (s->vcd_padding_bitrate > 0 && pts!=AV_NOPTS_VALUE) + { + int64_t full_pad_bytes; + + full_pad_bytes = (int64_t)((s->vcd_padding_bitrate * (pts / 90000.0)) / 8.0); //FIXME this is wrong + pad_bytes = (int) (full_pad_bytes - s->vcd_padding_bytes_written); + + if (pad_bytes<0) + /* might happen if we have already padded to a later timestamp. This + can occur if another stream has already advanced further.*/ + pad_bytes=0; + } + + return pad_bytes; +} + + +/* Write an MPEG padding packet header. */ +static void put_padding_packet(AVFormatContext *ctx, AVIOContext *pb,int packet_bytes) +{ + MpegMuxContext *s = ctx->priv_data; + int i; + + avio_wb32(pb, PADDING_STREAM); + avio_wb16(pb, packet_bytes - 6); + if (!s->is_mpeg2) { + avio_w8(pb, 0x0f); + packet_bytes -= 7; + } else + packet_bytes -= 6; + + for(i=0;ipremux_packet; + + while(len>0){ + if(pkt_desc->size == pkt_desc->unwritten_size) + nb_frames++; + len -= pkt_desc->unwritten_size; + pkt_desc= pkt_desc->next; + } + + return nb_frames; +} + +/* flush the packet on stream stream_index */ +static int flush_packet(AVFormatContext *ctx, int stream_index, + int64_t pts, int64_t dts, int64_t scr, int trailer_size) +{ + MpegMuxContext *s = ctx->priv_data; + StreamInfo *stream = ctx->streams[stream_index]->priv_data; + uint8_t *buf_ptr; + int size, payload_size, startcode, id, stuffing_size, i, header_len; + int packet_size; + uint8_t buffer[128]; + int zero_trail_bytes = 0; + int pad_packet_bytes = 0; + int pes_flags; + int general_pack = 0; /*"general" pack without data specific to one stream?*/ + int nb_frames; + + id = stream->id; + + av_dlog(ctx, "packet ID=%2x PTS=%0.3f\n", id, pts / 90000.0); + + buf_ptr = buffer; + + if ((s->packet_number % s->pack_header_freq) == 0 || s->last_scr != scr) { + /* output pack and systems header if needed */ + size = put_pack_header(ctx, buf_ptr, scr); + buf_ptr += size; + s->last_scr= scr; + + if (s->is_vcd) { + /* there is exactly one system header for each stream in a VCD MPEG, + One in the very first video packet and one in the very first + audio packet (see VCD standard p. IV-7 and IV-8).*/ + + if (stream->packet_number==0) { + size = put_system_header(ctx, buf_ptr, id); + buf_ptr += size; + } + } else if (s->is_dvd) { + if (stream->align_iframe || s->packet_number == 0){ + int PES_bytes_to_fill = s->packet_size - size - 10; + + if (pts != AV_NOPTS_VALUE) { + if (dts != pts) + PES_bytes_to_fill -= 5 + 5; + else + PES_bytes_to_fill -= 5; + } + + if (stream->bytes_to_iframe == 0 || s->packet_number == 0) { + size = put_system_header(ctx, buf_ptr, 0); + buf_ptr += size; + size = buf_ptr - buffer; + avio_write(ctx->pb, buffer, size); + + avio_wb32(ctx->pb, PRIVATE_STREAM_2); + avio_wb16(ctx->pb, 0x03d4); // length + avio_w8(ctx->pb, 0x00); // substream ID, 00=PCI + for (i = 0; i < 979; i++) + avio_w8(ctx->pb, 0x00); + + avio_wb32(ctx->pb, PRIVATE_STREAM_2); + avio_wb16(ctx->pb, 0x03fa); // length + avio_w8(ctx->pb, 0x01); // substream ID, 01=DSI + for (i = 0; i < 1017; i++) + avio_w8(ctx->pb, 0x00); + + memset(buffer, 0, 128); + buf_ptr = buffer; + s->packet_number++; + stream->align_iframe = 0; + scr += s->packet_size*90000LL / (s->mux_rate*50LL); //FIXME rounding and first few bytes of each packet + size = put_pack_header(ctx, buf_ptr, scr); + s->last_scr= scr; + buf_ptr += size; + /* GOP Start */ + } else if (stream->bytes_to_iframe < PES_bytes_to_fill) { + pad_packet_bytes = PES_bytes_to_fill - stream->bytes_to_iframe; + } + } + } else { + if ((s->packet_number % s->system_header_freq) == 0) { + size = put_system_header(ctx, buf_ptr, 0); + buf_ptr += size; + } + } + } + size = buf_ptr - buffer; + avio_write(ctx->pb, buffer, size); + + packet_size = s->packet_size - size; + + if (s->is_vcd && (id & 0xe0) == AUDIO_ID) + /* The VCD standard demands that 20 zero bytes follow + each audio pack (see standard p. IV-8).*/ + zero_trail_bytes += 20; + + if ((s->is_vcd && stream->packet_number==0) + || (s->is_svcd && s->packet_number==0)) { + /* for VCD the first pack of each stream contains only the pack header, + the system header and lots of padding (see VCD standard p. IV-6). + In the case of an audio pack, 20 zero bytes are also added at + the end.*/ + /* For SVCD we fill the very first pack to increase compatibility with + some DVD players. Not mandated by the standard.*/ + if (s->is_svcd) + general_pack = 1; /* the system header refers to both streams and no stream data*/ + pad_packet_bytes = packet_size - zero_trail_bytes; + } + + packet_size -= pad_packet_bytes + zero_trail_bytes; + + if (packet_size > 0) { + + /* packet header size */ + packet_size -= 6; + + /* packet header */ + if (s->is_mpeg2) { + header_len = 3; + if (stream->packet_number==0) + header_len += 3; /* PES extension */ + header_len += 1; /* obligatory stuffing byte */ + } else { + header_len = 0; + } + if (pts != AV_NOPTS_VALUE) { + if (dts != pts) + header_len += 5 + 5; + else + header_len += 5; + } else { + if (!s->is_mpeg2) + header_len++; + } + + payload_size = packet_size - header_len; + if (id < 0xc0) { + startcode = PRIVATE_STREAM_1; + payload_size -= 1; + if (id >= 0x40) { + payload_size -= 3; + if (id >= 0xa0) + payload_size -= 3; + } + } else { + startcode = 0x100 + id; + } + + stuffing_size = payload_size - av_fifo_size(stream->fifo); + + // first byte does not fit -> reset pts/dts + stuffing + if(payload_size <= trailer_size && pts != AV_NOPTS_VALUE){ + int timestamp_len=0; + if(dts != pts) + timestamp_len += 5; + if(pts != AV_NOPTS_VALUE) + timestamp_len += s->is_mpeg2 ? 5 : 4; + pts=dts= AV_NOPTS_VALUE; + header_len -= timestamp_len; + if (s->is_dvd && stream->align_iframe) { + pad_packet_bytes += timestamp_len; + packet_size -= timestamp_len; + } else { + payload_size += timestamp_len; + } + stuffing_size += timestamp_len; + if(payload_size > trailer_size) + stuffing_size += payload_size - trailer_size; + } + + if (pad_packet_bytes > 0 && pad_packet_bytes <= 7) { // can't use padding, so use stuffing + packet_size += pad_packet_bytes; + payload_size += pad_packet_bytes; // undo the previous adjustment + if (stuffing_size < 0) { + stuffing_size = pad_packet_bytes; + } else { + stuffing_size += pad_packet_bytes; + } + pad_packet_bytes = 0; + } + + if (stuffing_size < 0) + stuffing_size = 0; + + if (startcode == PRIVATE_STREAM_1 && id >= 0xa0) { + if (payload_size < av_fifo_size(stream->fifo)) + stuffing_size += payload_size % stream->lpcm_align; + } + + if (stuffing_size > 16) { /*<=16 for MPEG-1, <=32 for MPEG-2*/ + pad_packet_bytes += stuffing_size; + packet_size -= stuffing_size; + payload_size -= stuffing_size; + stuffing_size = 0; + } + + nb_frames= get_nb_frames(ctx, stream, payload_size - stuffing_size); + + avio_wb32(ctx->pb, startcode); + + avio_wb16(ctx->pb, packet_size); + + if (!s->is_mpeg2) + for(i=0;ipb, 0xff); + + if (s->is_mpeg2) { + avio_w8(ctx->pb, 0x80); /* mpeg2 id */ + + pes_flags=0; + + if (pts != AV_NOPTS_VALUE) { + pes_flags |= 0x80; + if (dts != pts) + pes_flags |= 0x40; + } + + /* Both the MPEG-2 and the SVCD standards demand that the + P-STD_buffer_size field be included in the first packet of + every stream. (see SVCD standard p. 26 V.2.3.1 and V.2.3.2 + and MPEG-2 standard 2.7.7) */ + if (stream->packet_number == 0) + pes_flags |= 0x01; + + avio_w8(ctx->pb, pes_flags); /* flags */ + avio_w8(ctx->pb, header_len - 3 + stuffing_size); + + if (pes_flags & 0x80) /*write pts*/ + put_timestamp(ctx->pb, (pes_flags & 0x40) ? 0x03 : 0x02, pts); + if (pes_flags & 0x40) /*write dts*/ + put_timestamp(ctx->pb, 0x01, dts); + + if (pes_flags & 0x01) { /*write pes extension*/ + avio_w8(ctx->pb, 0x10); /* flags */ + + /* P-STD buffer info */ + if ((id & 0xe0) == AUDIO_ID) + avio_wb16(ctx->pb, 0x4000 | stream->max_buffer_size/ 128); + else + avio_wb16(ctx->pb, 0x6000 | stream->max_buffer_size/1024); + } + + } else { + if (pts != AV_NOPTS_VALUE) { + if (dts != pts) { + put_timestamp(ctx->pb, 0x03, pts); + put_timestamp(ctx->pb, 0x01, dts); + } else { + put_timestamp(ctx->pb, 0x02, pts); + } + } else { + avio_w8(ctx->pb, 0x0f); + } + } + + if (s->is_mpeg2) { + /* special stuffing byte that is always written + to prevent accidental generation of start codes. */ + avio_w8(ctx->pb, 0xff); + + for(i=0;ipb, 0xff); + } + + if (startcode == PRIVATE_STREAM_1) { + avio_w8(ctx->pb, id); + if (id >= 0xa0) { + /* LPCM (XXX: check nb_frames) */ + avio_w8(ctx->pb, 7); + avio_wb16(ctx->pb, 4); /* skip 3 header bytes */ + avio_w8(ctx->pb, stream->lpcm_header[0]); + avio_w8(ctx->pb, stream->lpcm_header[1]); + avio_w8(ctx->pb, stream->lpcm_header[2]); + } else if (id >= 0x40) { + /* AC-3 */ + avio_w8(ctx->pb, nb_frames); + avio_wb16(ctx->pb, trailer_size+1); + } + } + + /* output data */ + assert(payload_size - stuffing_size <= av_fifo_size(stream->fifo)); + av_fifo_generic_read(stream->fifo, ctx->pb, payload_size - stuffing_size, (void*)avio_write); + stream->bytes_to_iframe -= payload_size - stuffing_size; + }else{ + payload_size= + stuffing_size= 0; + } + + if (pad_packet_bytes > 0) + put_padding_packet(ctx,ctx->pb, pad_packet_bytes); + + for(i=0;ipb, 0x00); + + avio_flush(ctx->pb); + + s->packet_number++; + + /* only increase the stream packet number if this pack actually contains + something that is specific to this stream! I.e. a dedicated header + or some data.*/ + if (!general_pack) + stream->packet_number++; + + return payload_size - stuffing_size; +} + +static void put_vcd_padding_sector(AVFormatContext *ctx) +{ + /* There are two ways to do this padding: writing a sector/pack + of 0 values, or writing an MPEG padding pack. Both seem to + work with most decoders, BUT the VCD standard only allows a 0-sector + (see standard p. IV-4, IV-5). + So a 0-sector it is...*/ + + MpegMuxContext *s = ctx->priv_data; + int i; + + for(i=0;ipacket_size;i++) + avio_w8(ctx->pb, 0); + + s->vcd_padding_bytes_written += s->packet_size; + + avio_flush(ctx->pb); + + /* increasing the packet number is correct. The SCR of the following packs + is calculated from the packet_number and it has to include the padding + sector (it represents the sector index, not the MPEG pack index) + (see VCD standard p. IV-6)*/ + s->packet_number++; +} + +static int remove_decoded_packets(AVFormatContext *ctx, int64_t scr){ +// MpegMuxContext *s = ctx->priv_data; + int i; + + for(i=0; inb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + PacketDesc *pkt_desc; + + while((pkt_desc= stream->predecode_packet) + && scr > pkt_desc->dts){ //FIXME > vs >= + if(stream->buffer_index < pkt_desc->size || + stream->predecode_packet == stream->premux_packet){ + av_log(ctx, AV_LOG_ERROR, + "buffer underflow i=%d bufi=%d size=%d\n", + i, stream->buffer_index, pkt_desc->size); + break; + } + stream->buffer_index -= pkt_desc->size; + + stream->predecode_packet= pkt_desc->next; + av_freep(&pkt_desc); + } + } + + return 0; +} + +static int output_packet(AVFormatContext *ctx, int flush){ + MpegMuxContext *s = ctx->priv_data; + AVStream *st; + StreamInfo *stream; + int i, avail_space=0, es_size, trailer_size; + int best_i= -1; + int best_score= INT_MIN; + int ignore_constraints=0; + int64_t scr= s->last_scr; + PacketDesc *timestamp_packet; + const int64_t max_delay= av_rescale(ctx->max_delay, 90000, AV_TIME_BASE); + +retry: + for(i=0; inb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + const int avail_data= av_fifo_size(stream->fifo); + const int space= stream->max_buffer_size - stream->buffer_index; + int rel_space= 1024LL*space / stream->max_buffer_size; + PacketDesc *next_pkt= stream->premux_packet; + + /* for subtitle, a single PES packet must be generated, + so we flush after every single subtitle packet */ + if(s->packet_size > avail_data && !flush + && st->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) + return 0; + if(avail_data==0) + continue; + assert(avail_data>0); + + if(space < s->packet_size && !ignore_constraints) + continue; + + if(next_pkt && next_pkt->dts - scr > max_delay) + continue; + + if(rel_space > best_score){ + best_score= rel_space; + best_i = i; + avail_space= space; + } + } + + if(best_i < 0){ + int64_t best_dts= INT64_MAX; + + for(i=0; inb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + PacketDesc *pkt_desc= stream->predecode_packet; + if(pkt_desc && pkt_desc->dts < best_dts) + best_dts= pkt_desc->dts; + } + + av_dlog(ctx, "bumping scr, scr:%f, dts:%f\n", + scr / 90000.0, best_dts / 90000.0); + if(best_dts == INT64_MAX) + return 0; + + if(scr >= best_dts+1 && !ignore_constraints){ + av_log(ctx, AV_LOG_ERROR, "packet too large, ignoring buffer limits to mux it\n"); + ignore_constraints= 1; + } + scr= FFMAX(best_dts+1, scr); + if(remove_decoded_packets(ctx, scr) < 0) + return -1; + goto retry; + } + + assert(best_i >= 0); + + st = ctx->streams[best_i]; + stream = st->priv_data; + + assert(av_fifo_size(stream->fifo) > 0); + + assert(avail_space >= s->packet_size || ignore_constraints); + + timestamp_packet= stream->premux_packet; + if(timestamp_packet->unwritten_size == timestamp_packet->size){ + trailer_size= 0; + }else{ + trailer_size= timestamp_packet->unwritten_size; + timestamp_packet= timestamp_packet->next; + } + + if(timestamp_packet){ + av_dlog(ctx, "dts:%f pts:%f scr:%f stream:%d\n", + timestamp_packet->dts / 90000.0, + timestamp_packet->pts / 90000.0, + scr / 90000.0, best_i); + es_size= flush_packet(ctx, best_i, timestamp_packet->pts, timestamp_packet->dts, scr, trailer_size); + }else{ + assert(av_fifo_size(stream->fifo) == trailer_size); + es_size= flush_packet(ctx, best_i, AV_NOPTS_VALUE, AV_NOPTS_VALUE, scr, trailer_size); + } + + if (s->is_vcd) { + /* Write one or more padding sectors, if necessary, to reach + the constant overall bitrate.*/ + int vcd_pad_bytes; + + while((vcd_pad_bytes = get_vcd_padding_size(ctx,stream->premux_packet->pts) ) >= s->packet_size){ //FIXME pts cannot be correct here + put_vcd_padding_sector(ctx); + s->last_scr += s->packet_size*90000LL / (s->mux_rate*50LL); //FIXME rounding and first few bytes of each packet + } + } + + stream->buffer_index += es_size; + s->last_scr += s->packet_size*90000LL / (s->mux_rate*50LL); //FIXME rounding and first few bytes of each packet + + while(stream->premux_packet && stream->premux_packet->unwritten_size <= es_size){ + es_size -= stream->premux_packet->unwritten_size; + stream->premux_packet= stream->premux_packet->next; + } + if(es_size) + stream->premux_packet->unwritten_size -= es_size; + + if(remove_decoded_packets(ctx, s->last_scr) < 0) + return -1; + + return 1; +} + +static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + MpegMuxContext *s = ctx->priv_data; + int stream_index= pkt->stream_index; + int size= pkt->size; + uint8_t *buf= pkt->data; + AVStream *st = ctx->streams[stream_index]; + StreamInfo *stream = st->priv_data; + int64_t pts, dts; + PacketDesc *pkt_desc; + int preload; + const int is_iframe = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY); + + preload = av_rescale(s->preload, 90000, AV_TIME_BASE); + + pts= pkt->pts; + dts= pkt->dts; + + if (s->last_scr == AV_NOPTS_VALUE) { + if (dts == AV_NOPTS_VALUE || (dts < preload && ctx->avoid_negative_ts) || s->is_dvd) { + if (dts != AV_NOPTS_VALUE) + s->preload += av_rescale(-dts, AV_TIME_BASE, 90000); + s->last_scr = 0; + } else { + s->last_scr = dts - preload; + s->preload = 0; + } + preload = av_rescale(s->preload, 90000, AV_TIME_BASE); + av_log(ctx, AV_LOG_DEBUG, "First SCR: %"PRId64" First DTS: %"PRId64"\n", s->last_scr, dts + preload); + } + + if (dts != AV_NOPTS_VALUE) dts += preload; + if (pts != AV_NOPTS_VALUE) pts += preload; + + av_dlog(ctx, "dts:%f pts:%f flags:%d stream:%d nopts:%d\n", + dts / 90000.0, pts / 90000.0, pkt->flags, + pkt->stream_index, pts != AV_NOPTS_VALUE); + if (!stream->premux_packet) + stream->next_packet = &stream->premux_packet; + *stream->next_packet= + pkt_desc= av_mallocz(sizeof(PacketDesc)); + pkt_desc->pts= pts; + pkt_desc->dts= dts; + pkt_desc->unwritten_size= + pkt_desc->size= size; + if(!stream->predecode_packet) + stream->predecode_packet= pkt_desc; + stream->next_packet= &pkt_desc->next; + + if (av_fifo_realloc2(stream->fifo, av_fifo_size(stream->fifo) + size) < 0) + return -1; + + if (s->is_dvd){ + if (is_iframe && (s->packet_number == 0 || (pts - stream->vobu_start_pts >= 36000))) { // min VOBU length 0.4 seconds (mpucoder) + stream->bytes_to_iframe = av_fifo_size(stream->fifo); + stream->align_iframe = 1; + stream->vobu_start_pts = pts; + } + } + + av_fifo_generic_write(stream->fifo, buf, size, NULL); + + for(;;){ + int ret= output_packet(ctx, 0); + if(ret<=0) + return ret; + } +} + +static int mpeg_mux_end(AVFormatContext *ctx) +{ +// MpegMuxContext *s = ctx->priv_data; + StreamInfo *stream; + int i; + + for(;;){ + int ret= output_packet(ctx, 1); + if(ret<0) + return ret; + else if(ret==0) + break; + } + + /* End header according to MPEG1 systems standard. We do not write + it as it is usually not needed by decoders and because it + complicates MPEG stream concatenation. */ + //avio_wb32(ctx->pb, ISO_11172_END_CODE); + //avio_flush(ctx->pb); + + for(i=0;inb_streams;i++) { + stream = ctx->streams[i]->priv_data; + + assert(av_fifo_size(stream->fifo) == 0); + av_fifo_free(stream->fifo); + } + return 0; +} + +#define OFFSET(x) offsetof(MpegMuxContext, x) +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "muxrate", NULL, OFFSET(user_mux_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, + { "preload", "Initial demux-decode delay in microseconds.", OFFSET(preload), AV_OPT_TYPE_INT, {.i64 = 500000}, 0, INT_MAX, E}, + { NULL }, +}; + +#define MPEGENC_CLASS(flavor)\ +static const AVClass flavor ## _class = {\ + .class_name = #flavor " muxer",\ + .item_name = av_default_item_name,\ + .version = LIBAVUTIL_VERSION_INT,\ + .option = options,\ +}; + +#if CONFIG_MPEG1SYSTEM_MUXER +MPEGENC_CLASS(mpeg) +AVOutputFormat ff_mpeg1system_muxer = { + .name = "mpeg", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream"), + .mime_type = "video/mpeg", + .extensions = "mpg,mpeg", + .priv_data_size = sizeof(MpegMuxContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_header = mpeg_mux_init, + .write_packet = mpeg_mux_write_packet, + .write_trailer = mpeg_mux_end, + .priv_class = &mpeg_class, +}; +#endif +#if CONFIG_MPEG1VCD_MUXER +MPEGENC_CLASS(vcd) +AVOutputFormat ff_mpeg1vcd_muxer = { + .name = "vcd", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 Systems / MPEG program stream (VCD)"), + .mime_type = "video/mpeg", + .priv_data_size = sizeof(MpegMuxContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_header = mpeg_mux_init, + .write_packet = mpeg_mux_write_packet, + .write_trailer = mpeg_mux_end, + .priv_class = &vcd_class, +}; +#endif +#if CONFIG_MPEG2VOB_MUXER +MPEGENC_CLASS(vob) +AVOutputFormat ff_mpeg2vob_muxer = { + .name = "vob", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (VOB)"), + .mime_type = "video/mpeg", + .extensions = "vob", + .priv_data_size = sizeof(MpegMuxContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mpeg_mux_init, + .write_packet = mpeg_mux_write_packet, + .write_trailer = mpeg_mux_end, + .priv_class = &vob_class, +}; +#endif + +/* Same as mpeg2vob_mux except that the pack size is 2324 */ +#if CONFIG_MPEG2SVCD_MUXER +MPEGENC_CLASS(svcd) +AVOutputFormat ff_mpeg2svcd_muxer = { + .name = "svcd", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (SVCD)"), + .mime_type = "video/mpeg", + .extensions = "vob", + .priv_data_size = sizeof(MpegMuxContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mpeg_mux_init, + .write_packet = mpeg_mux_write_packet, + .write_trailer = mpeg_mux_end, + .priv_class = &svcd_class, +}; +#endif + +/* Same as mpeg2vob_mux except the 'is_dvd' flag is set to produce NAV pkts */ +#if CONFIG_MPEG2DVD_MUXER +MPEGENC_CLASS(dvd) +AVOutputFormat ff_mpeg2dvd_muxer = { + .name = "dvd", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 PS (DVD VOB)"), + .mime_type = "video/mpeg", + .extensions = "dvd", + .priv_data_size = sizeof(MpegMuxContext), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mpeg_mux_init, + .write_packet = mpeg_mux_write_packet, + .write_trailer = mpeg_mux_end, + .priv_class = &dvd_class, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/mpegenc.o libavformat/mpegenc.o: libavformat/mpegenc.c \ + libavutil/fifo.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/log.h \ + libavutil/mathematics.h libavutil/opt.h libavutil/samplefmt.h \ + libavcodec/put_bits.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/common.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/log.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/dict.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h libavformat/mpeg.h libavutil/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegenc.o Binary file ffmpeg/libavformat/mpegenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegts.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegts.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,2313 @@ +/* + * MPEG2 transport stream (aka DVB) demuxer + * Copyright (c) 2002-2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/buffer.h" +#include "libavutil/crc.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/dict.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "mpegts.h" +#include "internal.h" +#include "avio_internal.h" +#include "seek.h" +#include "mpeg.h" +#include "isom.h" + +/* maximum size in which we look for synchronisation if + synchronisation is lost */ +#define MAX_RESYNC_SIZE 65536 + +#define MAX_PES_PAYLOAD 200*1024 + +#define MAX_MP4_DESCR_COUNT 16 + +enum MpegTSFilterType { + MPEGTS_PES, + MPEGTS_SECTION, +}; + +typedef struct MpegTSFilter MpegTSFilter; + +typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos); + +typedef struct MpegTSPESFilter { + PESCallback *pes_cb; + void *opaque; +} MpegTSPESFilter; + +typedef void SectionCallback(MpegTSFilter *f, const uint8_t *buf, int len); + +typedef void SetServiceCallback(void *opaque, int ret); + +typedef struct MpegTSSectionFilter { + int section_index; + int section_h_size; + uint8_t *section_buf; + unsigned int check_crc:1; + unsigned int end_of_section_reached:1; + SectionCallback *section_cb; + void *opaque; +} MpegTSSectionFilter; + +struct MpegTSFilter { + int pid; + int es_id; + int last_cc; /* last cc code (-1 if first packet) */ + enum MpegTSFilterType type; + union { + MpegTSPESFilter pes_filter; + MpegTSSectionFilter section_filter; + } u; +}; + +#define MAX_PIDS_PER_PROGRAM 64 +struct Program { + unsigned int id; //program id/service id + unsigned int nb_pids; + unsigned int pids[MAX_PIDS_PER_PROGRAM]; +}; + +struct MpegTSContext { + const AVClass *class; + /* user data */ + AVFormatContext *stream; + /** raw packet size, including FEC if present */ + int raw_packet_size; + + int pos47; + + /** if true, all pids are analyzed to find streams */ + int auto_guess; + + /** compute exact PCR for each transport stream packet */ + int mpeg2ts_compute_pcr; + + int64_t cur_pcr; /**< used to estimate the exact PCR */ + int pcr_incr; /**< used to estimate the exact PCR */ + + /* data needed to handle file based ts */ + /** stop parsing loop */ + int stop_parse; + /** packet containing Audio/Video data */ + AVPacket *pkt; + /** to detect seek */ + int64_t last_pos; + + /******************************************/ + /* private mpegts data */ + /* scan context */ + /** structure to keep track of Program->pids mapping */ + unsigned int nb_prg; + struct Program *prg; + + int8_t crc_validity[NB_PID_MAX]; + + /** filters for various streams specified by PMT + for the PAT and PMT */ + MpegTSFilter *pids[NB_PID_MAX]; + int current_pid; +}; + +static const AVOption options[] = { + {"compute_pcr", "Compute exact PCR for each transport stream packet.", offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_INT, + {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass mpegtsraw_class = { + .class_name = "mpegtsraw demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* TS stream handling */ + +enum MpegTSState { + MPEGTS_HEADER = 0, + MPEGTS_PESHEADER, + MPEGTS_PESHEADER_FILL, + MPEGTS_PAYLOAD, + MPEGTS_SKIP, +}; + +/* enough for PES header + length */ +#define PES_START_SIZE 6 +#define PES_HEADER_SIZE 9 +#define MAX_PES_HEADER_SIZE (9 + 255) + +typedef struct PESContext { + int pid; + int pcr_pid; /**< if -1 then all packets containing PCR are considered */ + int stream_type; + MpegTSContext *ts; + AVFormatContext *stream; + AVStream *st; + AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */ + enum MpegTSState state; + /* used to get the format */ + int data_index; + int flags; /**< copied to the AVPacket flags */ + int total_size; + int pes_header_size; + int extended_stream_id; + int64_t pts, dts; + int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */ + uint8_t header[MAX_PES_HEADER_SIZE]; + AVBufferRef *buffer; + SLConfigDescr sl; +} PESContext; + +extern AVInputFormat ff_mpegts_demuxer; + +static void clear_avprogram(MpegTSContext *ts, unsigned int programid) +{ + AVProgram *prg = NULL; + int i; + for(i=0; istream->nb_programs; i++) + if(ts->stream->programs[i]->id == programid){ + prg = ts->stream->programs[i]; + break; + } + if (!prg) + return; + prg->nb_stream_indexes = 0; +} + +static void clear_program(MpegTSContext *ts, unsigned int programid) +{ + int i; + + clear_avprogram(ts, programid); + for(i=0; inb_prg; i++) + if(ts->prg[i].id == programid) + ts->prg[i].nb_pids = 0; +} + +static void clear_programs(MpegTSContext *ts) +{ + av_freep(&ts->prg); + ts->nb_prg=0; +} + +static void add_pat_entry(MpegTSContext *ts, unsigned int programid) +{ + struct Program *p; + void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(struct Program)); + if(!tmp) + return; + ts->prg = tmp; + p = &ts->prg[ts->nb_prg]; + p->id = programid; + p->nb_pids = 0; + ts->nb_prg++; +} + +static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid) +{ + int i; + struct Program *p = NULL; + for(i=0; inb_prg; i++) { + if(ts->prg[i].id == programid) { + p = &ts->prg[i]; + break; + } + } + if(!p) + return; + + if(p->nb_pids >= MAX_PIDS_PER_PROGRAM) + return; + p->pids[p->nb_pids++] = pid; +} + +static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +{ + int i; + for(i=0; inb_programs; i++) { + if(s->programs[i]->id == programid) { + s->programs[i]->pcr_pid = pid; + break; + } + } +} + +/** + * @brief discard_pid() decides if the pid is to be discarded according + * to caller's programs selection + * @param ts : - TS context + * @param pid : - pid + * @return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL + * 0 otherwise + */ +static int discard_pid(MpegTSContext *ts, unsigned int pid) +{ + int i, j, k; + int used = 0, discarded = 0; + struct Program *p; + for(i=0; inb_prg; i++) { + p = &ts->prg[i]; + for(j=0; jnb_pids; j++) { + if(p->pids[j] != pid) + continue; + //is program with id p->id set to be discarded? + for(k=0; kstream->nb_programs; k++) { + if(ts->stream->programs[k]->id == p->id) { + if(ts->stream->programs[k]->discard == AVDISCARD_ALL) + discarded++; + else + used++; + } + } + } + } + + return !used && discarded; +} + +/** + * Assemble PES packets out of TS packets, and then call the "section_cb" + * function when they are complete. + */ +static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, + const uint8_t *buf, int buf_size, int is_start) +{ + MpegTSContext *ts = s->priv_data; + MpegTSSectionFilter *tss = &tss1->u.section_filter; + int len; + + if (is_start) { + memcpy(tss->section_buf, buf, buf_size); + tss->section_index = buf_size; + tss->section_h_size = -1; + tss->end_of_section_reached = 0; + } else { + if (tss->end_of_section_reached) + return; + len = 4096 - tss->section_index; + if (buf_size < len) + len = buf_size; + memcpy(tss->section_buf + tss->section_index, buf, len); + tss->section_index += len; + } + + /* compute section length if possible */ + if (tss->section_h_size == -1 && tss->section_index >= 3) { + len = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3; + if (len > 4096) + return; + tss->section_h_size = len; + } + + if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { + int crc_valid = 1; + tss->end_of_section_reached = 1; + + if (tss->check_crc){ + crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size); + if (crc_valid){ + ts->crc_validity[ tss1->pid ] = 100; + }else if(ts->crc_validity[ tss1->pid ] > -10){ + ts->crc_validity[ tss1->pid ]--; + }else + crc_valid = 2; + } + if (crc_valid) + tss->section_cb(tss1, tss->section_buf, tss->section_h_size); + } +} + +static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid, + SectionCallback *section_cb, void *opaque, + int check_crc) + +{ + MpegTSFilter *filter; + MpegTSSectionFilter *sec; + + av_dlog(ts->stream, "Filter: pid=0x%x\n", pid); + + if (pid >= NB_PID_MAX || ts->pids[pid]) + return NULL; + filter = av_mallocz(sizeof(MpegTSFilter)); + if (!filter) + return NULL; + ts->pids[pid] = filter; + filter->type = MPEGTS_SECTION; + filter->pid = pid; + filter->es_id = -1; + filter->last_cc = -1; + sec = &filter->u.section_filter; + sec->section_cb = section_cb; + sec->opaque = opaque; + sec->section_buf = av_malloc(MAX_SECTION_SIZE); + sec->check_crc = check_crc; + if (!sec->section_buf) { + av_free(filter); + return NULL; + } + return filter; +} + +static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid, + PESCallback *pes_cb, + void *opaque) +{ + MpegTSFilter *filter; + MpegTSPESFilter *pes; + + if (pid >= NB_PID_MAX || ts->pids[pid]) + return NULL; + filter = av_mallocz(sizeof(MpegTSFilter)); + if (!filter) + return NULL; + ts->pids[pid] = filter; + filter->type = MPEGTS_PES; + filter->pid = pid; + filter->es_id = -1; + filter->last_cc = -1; + pes = &filter->u.pes_filter; + pes->pes_cb = pes_cb; + pes->opaque = opaque; + return filter; +} + +static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) +{ + int pid; + + pid = filter->pid; + if (filter->type == MPEGTS_SECTION) + av_freep(&filter->u.section_filter.section_buf); + else if (filter->type == MPEGTS_PES) { + PESContext *pes = filter->u.pes_filter.opaque; + av_buffer_unref(&pes->buffer); + /* referenced private data will be freed later in + * avformat_close_input */ + if (!((PESContext *)filter->u.pes_filter.opaque)->st) { + av_freep(&filter->u.pes_filter.opaque); + } + } + + av_free(filter); + ts->pids[pid] = NULL; +} + +static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ + int stat[TS_MAX_PACKET_SIZE]; + int i; + int x=0; + int best_score=0; + + memset(stat, 0, packet_size*sizeof(int)); + + for(x=i=0; i best_score){ + best_score= stat[x]; + if(index) *index= x; + } + } + + x++; + if(x == packet_size) x= 0; + } + + return best_score; +} + +/* autodetect fec presence. Must have at least 1024 bytes */ +static int get_packet_size(const uint8_t *buf, int size) +{ + int score, fec_score, dvhs_score; + + if (size < (TS_FEC_PACKET_SIZE * 5 + 1)) + return -1; + + score = analyze(buf, size, TS_PACKET_SIZE, NULL); + dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL); + fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL); + av_dlog(NULL, "score: %d, dvhs_score: %d, fec_score: %d \n", + score, dvhs_score, fec_score); + + if (score > fec_score && score > dvhs_score) return TS_PACKET_SIZE; + else if(dvhs_score > score && dvhs_score > fec_score) return TS_DVHS_PACKET_SIZE; + else if(score < fec_score && dvhs_score < fec_score) return TS_FEC_PACKET_SIZE; + else return -1; +} + +typedef struct SectionHeader { + uint8_t tid; + uint16_t id; + uint8_t version; + uint8_t sec_num; + uint8_t last_sec_num; +} SectionHeader; + +static inline int get8(const uint8_t **pp, const uint8_t *p_end) +{ + const uint8_t *p; + int c; + + p = *pp; + if (p >= p_end) + return -1; + c = *p++; + *pp = p; + return c; +} + +static inline int get16(const uint8_t **pp, const uint8_t *p_end) +{ + const uint8_t *p; + int c; + + p = *pp; + if ((p + 1) >= p_end) + return -1; + c = AV_RB16(p); + p += 2; + *pp = p; + return c; +} + +/* read and allocate a DVB string preceded by its length */ +static char *getstr8(const uint8_t **pp, const uint8_t *p_end) +{ + int len; + const uint8_t *p; + char *str; + + p = *pp; + len = get8(&p, p_end); + if (len < 0) + return NULL; + if ((p + len) > p_end) + return NULL; + str = av_malloc(len + 1); + if (!str) + return NULL; + memcpy(str, p, len); + str[len] = '\0'; + p += len; + *pp = p; + return str; +} + +static int parse_section_header(SectionHeader *h, + const uint8_t **pp, const uint8_t *p_end) +{ + int val; + + val = get8(pp, p_end); + if (val < 0) + return -1; + h->tid = val; + *pp += 2; + val = get16(pp, p_end); + if (val < 0) + return -1; + h->id = val; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->version = (val >> 1) & 0x1f; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->sec_num = val; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->last_sec_num = val; + return 0; +} + +typedef struct { + uint32_t stream_type; + enum AVMediaType codec_type; + enum AVCodecID codec_id; +} StreamType; + +static const StreamType ISO_types[] = { + { 0x01, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO }, + { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO }, + { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, + { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, + { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, + { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + /* Makito encoder sets stream type 0x11 for AAC, + * so auto-detect LOAS/LATM instead of hardcoding it. */ +#if !CONFIG_LOAS_DEMUXER + { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ +#endif + { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, + { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, + { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, +}; + +static const StreamType HDMV_types[] = { + { 0x80, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_BLURAY }, + { 0x81, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { 0x82, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0x83, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_TRUEHD }, + { 0x84, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, + { 0x85, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD */ + { 0x86, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD MASTER*/ + { 0xa1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC3 Secondary Audio */ + { 0xa2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS Express Secondary Audio */ + { 0x90, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, + { 0 }, +}; + +/* ATSC ? */ +static const StreamType MISC_types[] = { + { 0x81, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { 0x8a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0 }, +}; + +static const StreamType REGD_types[] = { + { MKTAG('d','r','a','c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { MKTAG('A','C','-','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { MKTAG('B','S','S','D'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_S302M }, + { MKTAG('D','T','S','1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('D','T','S','2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('D','T','S','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('K','L','V','A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, + { MKTAG('V','C','-','1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, +}; + +/* descriptor present */ +static const StreamType DESC_types[] = { + { 0x6a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, /* AC-3 descriptor */ + { 0x7a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ + { 0x7b, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT }, + { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ + { 0 }, +}; + +static void mpegts_find_stream_type(AVStream *st, + uint32_t stream_type, const StreamType *types) +{ + if (avcodec_is_open(st->codec)) { + av_log(NULL, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return; + } + + for (; types->stream_type; types++) { + if (stream_type == types->stream_type) { + st->codec->codec_type = types->codec_type; + st->codec->codec_id = types->codec_id; + st->request_probe = 0; + return; + } + } +} + +static int mpegts_set_stream_info(AVStream *st, PESContext *pes, + uint32_t stream_type, uint32_t prog_reg_desc) +{ + int old_codec_type= st->codec->codec_type; + int old_codec_id = st->codec->codec_id; + + if (avcodec_is_open(st->codec)) { + av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return 0; + } + + avpriv_set_pts_info(st, 33, 1, 90000); + st->priv_data = pes; + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_NONE; + st->need_parsing = AVSTREAM_PARSE_FULL; + pes->st = st; + pes->stream_type = stream_type; + + av_log(pes->stream, AV_LOG_DEBUG, + "stream=%d stream_type=%x pid=%x prog_reg_desc=%.4s\n", + st->index, pes->stream_type, pes->pid, (char*)&prog_reg_desc); + + st->codec->codec_tag = pes->stream_type; + + mpegts_find_stream_type(st, pes->stream_type, ISO_types); + if ((prog_reg_desc == AV_RL32("HDMV") || + prog_reg_desc == AV_RL32("HDPR")) && + st->codec->codec_id == AV_CODEC_ID_NONE) { + mpegts_find_stream_type(st, pes->stream_type, HDMV_types); + if (pes->stream_type == 0x83) { + // HDMV TrueHD streams also contain an AC3 coded version of the + // audio track - add a second stream for this + AVStream *sub_st; + // priv_data cannot be shared between streams + PESContext *sub_pes = av_malloc(sizeof(*sub_pes)); + if (!sub_pes) + return AVERROR(ENOMEM); + memcpy(sub_pes, pes, sizeof(*sub_pes)); + + sub_st = avformat_new_stream(pes->stream, NULL); + if (!sub_st) { + av_free(sub_pes); + return AVERROR(ENOMEM); + } + + sub_st->id = pes->pid; + avpriv_set_pts_info(sub_st, 33, 1, 90000); + sub_st->priv_data = sub_pes; + sub_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + sub_st->codec->codec_id = AV_CODEC_ID_AC3; + sub_st->need_parsing = AVSTREAM_PARSE_FULL; + sub_pes->sub_st = pes->sub_st = sub_st; + } + } + if (st->codec->codec_id == AV_CODEC_ID_NONE) + mpegts_find_stream_type(st, pes->stream_type, MISC_types); + if (st->codec->codec_id == AV_CODEC_ID_NONE){ + st->codec->codec_id = old_codec_id; + st->codec->codec_type= old_codec_type; + } + + return 0; +} + +static void new_pes_packet(PESContext *pes, AVPacket *pkt) +{ + av_init_packet(pkt); + + pkt->buf = pes->buffer; + pkt->data = pes->buffer->data; + pkt->size = pes->data_index; + + if(pes->total_size != MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index != pes->total_size + PES_START_SIZE) { + av_log(pes->stream, AV_LOG_WARNING, "PES packet size mismatch\n"); + pes->flags |= AV_PKT_FLAG_CORRUPT; + } + memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID + if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76) + pkt->stream_index = pes->sub_st->index; + else + pkt->stream_index = pes->st->index; + pkt->pts = pes->pts; + pkt->dts = pes->dts; + /* store position of first TS packet of this PES packet */ + pkt->pos = pes->ts_packet_pos; + pkt->flags = pes->flags; + + /* reset pts values */ + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + pes->buffer = NULL; + pes->data_index = 0; + pes->flags = 0; +} + +static uint64_t get_ts64(GetBitContext *gb, int bits) +{ + if (get_bits_left(gb) < bits) + return AV_NOPTS_VALUE; + return get_bits64(gb, bits); +} + +static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf, int buf_size) +{ + GetBitContext gb; + int au_start_flag = 0, au_end_flag = 0, ocr_flag = 0, idle_flag = 0; + int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0; + int dts_flag = -1, cts_flag = -1; + int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE; + + init_get_bits(&gb, buf, buf_size*8); + + if (sl->use_au_start) + au_start_flag = get_bits1(&gb); + if (sl->use_au_end) + au_end_flag = get_bits1(&gb); + if (!sl->use_au_start && !sl->use_au_end) + au_start_flag = au_end_flag = 1; + if (sl->ocr_len > 0) + ocr_flag = get_bits1(&gb); + if (sl->use_idle) + idle_flag = get_bits1(&gb); + if (sl->use_padding) + padding_flag = get_bits1(&gb); + if (padding_flag) + padding_bits = get_bits(&gb, 3); + + if (!idle_flag && (!padding_flag || padding_bits != 0)) { + if (sl->packet_seq_num_len) + skip_bits_long(&gb, sl->packet_seq_num_len); + if (sl->degr_prior_len) + if (get_bits1(&gb)) + skip_bits(&gb, sl->degr_prior_len); + if (ocr_flag) + skip_bits_long(&gb, sl->ocr_len); + if (au_start_flag) { + if (sl->use_rand_acc_pt) + get_bits1(&gb); + if (sl->au_seq_num_len > 0) + skip_bits_long(&gb, sl->au_seq_num_len); + if (sl->use_timestamps) { + dts_flag = get_bits1(&gb); + cts_flag = get_bits1(&gb); + } + } + if (sl->inst_bitrate_len) + inst_bitrate_flag = get_bits1(&gb); + if (dts_flag == 1) + dts = get_ts64(&gb, sl->timestamp_len); + if (cts_flag == 1) + cts = get_ts64(&gb, sl->timestamp_len); + if (sl->au_len > 0) + skip_bits_long(&gb, sl->au_len); + if (inst_bitrate_flag) + skip_bits_long(&gb, sl->inst_bitrate_len); + } + + if (dts != AV_NOPTS_VALUE) + pes->dts = dts; + if (cts != AV_NOPTS_VALUE) + pes->pts = cts; + + if (sl->timestamp_len && sl->timestamp_res) + avpriv_set_pts_info(pes->st, sl->timestamp_len, 1, sl->timestamp_res); + + return (get_bits_count(&gb) + 7) >> 3; +} + +/* return non zero if a packet could be constructed */ +static int mpegts_push_data(MpegTSFilter *filter, + const uint8_t *buf, int buf_size, int is_start, + int64_t pos) +{ + PESContext *pes = filter->u.pes_filter.opaque; + MpegTSContext *ts = pes->ts; + const uint8_t *p; + int len, code; + + if(!ts->pkt) + return 0; + + if (is_start) { + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, ts->pkt); + ts->stop_parse = 1; + } + pes->state = MPEGTS_HEADER; + pes->data_index = 0; + pes->ts_packet_pos = pos; + } + p = buf; + while (buf_size > 0) { + switch(pes->state) { + case MPEGTS_HEADER: + len = PES_START_SIZE - pes->data_index; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_START_SIZE) { + /* we got all the PES or section header. We can now + decide */ + if (pes->header[0] == 0x00 && pes->header[1] == 0x00 && + pes->header[2] == 0x01) { + /* it must be an mpeg2 PES stream */ + code = pes->header[3] | 0x100; + av_dlog(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code); + + if ((pes->st && pes->st->discard == AVDISCARD_ALL && + (!pes->sub_st || pes->sub_st->discard == AVDISCARD_ALL)) || + code == 0x1be) /* padding_stream */ + goto skip; + + /* stream not present in PMT */ + if (!pes->st) { + pes->st = avformat_new_stream(ts->stream, NULL); + if (!pes->st) + return AVERROR(ENOMEM); + pes->st->id = pes->pid; + mpegts_set_stream_info(pes->st, pes, 0, 0); + } + + pes->total_size = AV_RB16(pes->header + 4); + /* NOTE: a zero total size means the PES size is + unbounded */ + if (!pes->total_size) + pes->total_size = MAX_PES_PAYLOAD; + + /* allocate pes buffer */ + pes->buffer = av_buffer_alloc(pes->total_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + + if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */ + code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */ + code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ + code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ + pes->state = MPEGTS_PESHEADER; + if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) { + av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", + pes->pid, pes->stream_type); + pes->st->request_probe= 1; + } + } else { + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + } + } else { + /* otherwise, it should be a table */ + /* skip packet */ + skip: + pes->state = MPEGTS_SKIP; + continue; + } + } + break; + /**********************************************/ + /* PES packing parsing */ + case MPEGTS_PESHEADER: + len = PES_HEADER_SIZE - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_HEADER_SIZE) { + pes->pes_header_size = pes->header[8] + 9; + pes->state = MPEGTS_PESHEADER_FILL; + } + break; + case MPEGTS_PESHEADER_FILL: + len = pes->pes_header_size - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == pes->pes_header_size) { + const uint8_t *r; + unsigned int flags, pes_ext, skip; + + flags = pes->header[7]; + r = pes->header + 9; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + if ((flags & 0xc0) == 0x80) { + pes->dts = pes->pts = ff_parse_pes_pts(r); + r += 5; + } else if ((flags & 0xc0) == 0xc0) { + pes->pts = ff_parse_pes_pts(r); + r += 5; + pes->dts = ff_parse_pes_pts(r); + r += 5; + } + pes->extended_stream_id = -1; + if (flags & 0x01) { /* PES extension */ + pes_ext = *r++; + /* Skip PES private data, program packet sequence counter and P-STD buffer */ + skip = (pes_ext >> 4) & 0xb; + skip += skip & 0x9; + r += skip; + if ((pes_ext & 0x41) == 0x01 && + (r + 2) <= (pes->header + pes->pes_header_size)) { + /* PES extension 2 */ + if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0) + pes->extended_stream_id = r[1]; + } + } + + /* we got the full header. We parse it and get the payload */ + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + if (pes->stream_type == 0x12 && buf_size > 0) { + int sl_header_bytes = read_sl_header(pes, &pes->sl, p, buf_size); + pes->pes_header_size += sl_header_bytes; + p += sl_header_bytes; + buf_size -= sl_header_bytes; + } + } + break; + case MPEGTS_PAYLOAD: + if (buf_size > 0 && pes->buffer) { + if (pes->data_index > 0 && pes->data_index+buf_size > pes->total_size) { + new_pes_packet(pes, ts->pkt); + pes->total_size = MAX_PES_PAYLOAD; + pes->buffer = av_buffer_alloc(pes->total_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + ts->stop_parse = 1; + } else if (pes->data_index == 0 && buf_size > pes->total_size) { + // pes packet size is < ts size packet and pes data is padded with 0xff + // not sure if this is legal in ts but see issue #2392 + buf_size = pes->total_size; + } + memcpy(pes->buffer->data + pes->data_index, p, buf_size); + pes->data_index += buf_size; + } + buf_size = 0; + /* emit complete packets with known packet size + * decreases demuxer delay for infrequent packets like subtitles from + * a couple of seconds to milliseconds for properly muxed files. + * total_size is the number of bytes following pes_packet_length + * in the pes header, i.e. not counting the first PES_START_SIZE bytes */ + if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) { + ts->stop_parse = 1; + new_pes_packet(pes, ts->pkt); + } + break; + case MPEGTS_SKIP: + buf_size = 0; + break; + } + } + + return 0; +} + +static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid) +{ + MpegTSFilter *tss; + PESContext *pes; + + /* if no pid found, then add a pid context */ + pes = av_mallocz(sizeof(PESContext)); + if (!pes) + return 0; + pes->ts = ts; + pes->stream = ts->stream; + pes->pid = pid; + pes->pcr_pid = pcr_pid; + pes->state = MPEGTS_SKIP; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); + if (!tss) { + av_free(pes); + return 0; + } + return pes; +} + +#define MAX_LEVEL 4 +typedef struct { + AVFormatContext *s; + AVIOContext pb; + Mp4Descr *descr; + Mp4Descr *active_descr; + int descr_count; + int max_descr_count; + int level; +} MP4DescrParseContext; + +static int init_MP4DescrParseContext( + MP4DescrParseContext *d, AVFormatContext *s, const uint8_t *buf, + unsigned size, Mp4Descr *descr, int max_descr_count) +{ + int ret; + if (size > (1<<30)) + return AVERROR_INVALIDDATA; + + if ((ret = ffio_init_context(&d->pb, (unsigned char*)buf, size, 0, + NULL, NULL, NULL, NULL)) < 0) + return ret; + + d->s = s; + d->level = 0; + d->descr_count = 0; + d->descr = descr; + d->active_descr = NULL; + d->max_descr_count = max_descr_count; + + return 0; +} + +static void update_offsets(AVIOContext *pb, int64_t *off, int *len) { + int64_t new_off = avio_tell(pb); + (*len) -= new_off - *off; + *off = new_off; +} + +static int parse_mp4_descr(MP4DescrParseContext *d, int64_t off, int len, + int target_tag); + +static int parse_mp4_descr_arr(MP4DescrParseContext *d, int64_t off, int len) +{ + while (len > 0) { + if (parse_mp4_descr(d, off, len, 0) < 0) + return -1; + update_offsets(&d->pb, &off, &len); + } + return 0; +} + +static int parse_MP4IODescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + avio_rb16(&d->pb); // ID + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + update_offsets(&d->pb, &off, &len); + return parse_mp4_descr_arr(d, off, len); +} + +static int parse_MP4ODescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + int id_flags; + if (len < 2) + return 0; + id_flags = avio_rb16(&d->pb); + if (!(id_flags & 0x0020)) { //URL_Flag + update_offsets(&d->pb, &off, &len); + return parse_mp4_descr_arr(d, off, len); //ES_Descriptor[] + } else { + return 0; + } +} + +static int parse_MP4ESDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + int es_id = 0; + if (d->descr_count >= d->max_descr_count) + return -1; + ff_mp4_parse_es_descr(&d->pb, &es_id); + d->active_descr = d->descr + (d->descr_count++); + + d->active_descr->es_id = es_id; + update_offsets(&d->pb, &off, &len); + parse_mp4_descr(d, off, len, MP4DecConfigDescrTag); + update_offsets(&d->pb, &off, &len); + if (len > 0) + parse_mp4_descr(d, off, len, MP4SLDescrTag); + d->active_descr = NULL; + return 0; +} + +static int parse_MP4DecConfigDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + Mp4Descr *descr = d->active_descr; + if (!descr) + return -1; + d->active_descr->dec_config_descr = av_malloc(len); + if (!descr->dec_config_descr) + return AVERROR(ENOMEM); + descr->dec_config_descr_len = len; + avio_read(&d->pb, descr->dec_config_descr, len); + return 0; +} + +static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + Mp4Descr *descr = d->active_descr; + int predefined; + if (!descr) + return -1; + + predefined = avio_r8(&d->pb); + if (!predefined) { + int lengths; + int flags = avio_r8(&d->pb); + descr->sl.use_au_start = !!(flags & 0x80); + descr->sl.use_au_end = !!(flags & 0x40); + descr->sl.use_rand_acc_pt = !!(flags & 0x20); + descr->sl.use_padding = !!(flags & 0x08); + descr->sl.use_timestamps = !!(flags & 0x04); + descr->sl.use_idle = !!(flags & 0x02); + descr->sl.timestamp_res = avio_rb32(&d->pb); + avio_rb32(&d->pb); + descr->sl.timestamp_len = avio_r8(&d->pb); + descr->sl.ocr_len = avio_r8(&d->pb); + descr->sl.au_len = avio_r8(&d->pb); + descr->sl.inst_bitrate_len = avio_r8(&d->pb); + lengths = avio_rb16(&d->pb); + descr->sl.degr_prior_len = lengths >> 12; + descr->sl.au_seq_num_len = (lengths >> 7) & 0x1f; + descr->sl.packet_seq_num_len = (lengths >> 2) & 0x1f; + } else { + avpriv_report_missing_feature(d->s, "Predefined SLConfigDescriptor"); + } + return 0; +} + +static int parse_mp4_descr(MP4DescrParseContext *d, int64_t off, int len, + int target_tag) { + int tag; + int len1 = ff_mp4_read_descr(d->s, &d->pb, &tag); + update_offsets(&d->pb, &off, &len); + if (len < 0 || len1 > len || len1 <= 0) { + av_log(d->s, AV_LOG_ERROR, "Tag %x length violation new length %d bytes remaining %d\n", tag, len1, len); + return -1; + } + + if (d->level++ >= MAX_LEVEL) { + av_log(d->s, AV_LOG_ERROR, "Maximum MP4 descriptor level exceeded\n"); + goto done; + } + + if (target_tag && tag != target_tag) { + av_log(d->s, AV_LOG_ERROR, "Found tag %x expected %x\n", tag, target_tag); + goto done; + } + + switch (tag) { + case MP4IODescrTag: + parse_MP4IODescrTag(d, off, len1); + break; + case MP4ODescrTag: + parse_MP4ODescrTag(d, off, len1); + break; + case MP4ESDescrTag: + parse_MP4ESDescrTag(d, off, len1); + break; + case MP4DecConfigDescrTag: + parse_MP4DecConfigDescrTag(d, off, len1); + break; + case MP4SLDescrTag: + parse_MP4SLDescrTag(d, off, len1); + break; + } + +done: + d->level--; + avio_seek(&d->pb, off + len1, SEEK_SET); + return 0; +} + +static int mp4_read_iods(AVFormatContext *s, const uint8_t *buf, unsigned size, + Mp4Descr *descr, int *descr_count, int max_descr_count) +{ + MP4DescrParseContext d; + if (init_MP4DescrParseContext(&d, s, buf, size, descr, max_descr_count) < 0) + return -1; + + parse_mp4_descr(&d, avio_tell(&d.pb), size, MP4IODescrTag); + + *descr_count = d.descr_count; + return 0; +} + +static int mp4_read_od(AVFormatContext *s, const uint8_t *buf, unsigned size, + Mp4Descr *descr, int *descr_count, int max_descr_count) +{ + MP4DescrParseContext d; + if (init_MP4DescrParseContext(&d, s, buf, size, descr, max_descr_count) < 0) + return -1; + + parse_mp4_descr_arr(&d, avio_tell(&d.pb), size); + + *descr_count = d.descr_count; + return 0; +} + +static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h; + const uint8_t *p, *p_end; + AVIOContext pb; + Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = {{ 0 }}; + int mp4_descr_count = 0; + int i, pid; + AVFormatContext *s = ts->stream; + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(&h, &p, p_end) < 0) + return; + if (h.tid != M4OD_TID) + return; + + mp4_read_od(s, p, (unsigned)(p_end - p), mp4_descr, &mp4_descr_count, MAX_MP4_DESCR_COUNT); + + for (pid = 0; pid < NB_PID_MAX; pid++) { + if (!ts->pids[pid]) + continue; + for (i = 0; i < mp4_descr_count; i++) { + PESContext *pes; + AVStream *st; + if (ts->pids[pid]->es_id != mp4_descr[i].es_id) + continue; + if (!(ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES)) { + av_log(s, AV_LOG_ERROR, "pid %x is not PES\n", pid); + continue; + } + pes = ts->pids[pid]->u.pes_filter.opaque; + st = pes->st; + if (!st) { + continue; + } + + pes->sl = mp4_descr[i].sl; + + ffio_init_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(s, st, &pb); + if (st->codec->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0) + st->need_parsing = 0; + if (st->codec->codec_id == AV_CODEC_ID_H264 && + st->codec->extradata_size > 0) + st->need_parsing = 0; + + if (st->codec->codec_id <= AV_CODEC_ID_NONE) { + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_AUDIO) { + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_SUBTITLE) { + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_UNKNOWN) { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + } + } + } + for (i = 0; i < mp4_descr_count; i++) + av_free(mp4_descr[i].dec_config_descr); +} + +int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, + const uint8_t **pp, const uint8_t *desc_list_end, + Mp4Descr *mp4_descr, int mp4_descr_count, int pid, + MpegTSContext *ts) +{ + const uint8_t *desc_end; + int desc_len, desc_tag, desc_es_id; + char language[252]; + int i; + + desc_tag = get8(pp, desc_list_end); + if (desc_tag < 0) + return -1; + desc_len = get8(pp, desc_list_end); + if (desc_len < 0) + return -1; + desc_end = *pp + desc_len; + if (desc_end > desc_list_end) + return -1; + + av_dlog(fc, "tag: 0x%02x len=%d\n", desc_tag, desc_len); + + if (st->codec->codec_id == AV_CODEC_ID_NONE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type(st, desc_tag, DESC_types); + + switch(desc_tag) { + case 0x1E: /* SL descriptor */ + desc_es_id = get16(pp, desc_end); + if (ts && ts->pids[pid]) + ts->pids[pid]->es_id = desc_es_id; + for (i = 0; i < mp4_descr_count; i++) + if (mp4_descr[i].dec_config_descr_len && + mp4_descr[i].es_id == desc_es_id) { + AVIOContext pb; + ffio_init_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(fc, st, &pb); + if (st->codec->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0) + st->need_parsing = 0; + if (st->codec->codec_id == AV_CODEC_ID_MPEG4SYSTEMS) + mpegts_open_section_filter(ts, pid, m4sl_cb, ts, 1); + } + break; + case 0x1F: /* FMC descriptor */ + get16(pp, desc_end); + if (mp4_descr_count > 0 && (st->codec->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe>0) && + mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { + AVIOContext pb; + ffio_init_context(&pb, mp4_descr->dec_config_descr, + mp4_descr->dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(fc, st, &pb); + if (st->codec->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0){ + st->request_probe= st->need_parsing = 0; + st->codec->codec_type= AVMEDIA_TYPE_AUDIO; + } + } + break; + case 0x56: /* DVB teletext descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + av_dict_set(&st->metadata, "language", language, 0); + break; + case 0x59: /* subtitling descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + /* hearing impaired subtitles detection */ + switch(get8(pp, desc_end)) { + case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */ + case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */ + case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */ + case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */ + case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */ + case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */ + st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + break; + } + if (st->codec->extradata) { + if (st->codec->extradata_size == 4 && memcmp(st->codec->extradata, *pp, 4)) + avpriv_request_sample(fc, "DVB sub with multiple IDs"); + } else { + st->codec->extradata = av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) { + st->codec->extradata_size = 4; + memcpy(st->codec->extradata, *pp, 4); + } + } + *pp += 4; + av_dict_set(&st->metadata, "language", language, 0); + break; + case 0x0a: /* ISO 639 language descriptor */ + for (i = 0; i + 4 <= desc_len; i += 4) { + language[i + 0] = get8(pp, desc_end); + language[i + 1] = get8(pp, desc_end); + language[i + 2] = get8(pp, desc_end); + language[i + 3] = ','; + switch (get8(pp, desc_end)) { + case 0x01: st->disposition |= AV_DISPOSITION_CLEAN_EFFECTS; break; + case 0x02: st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; break; + case 0x03: st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; break; + } + } + if (i) { + language[i - 1] = 0; + av_dict_set(&st->metadata, "language", language, 0); + } + break; + case 0x05: /* registration descriptor */ + st->codec->codec_tag = bytestream_get_le32(pp); + av_dlog(fc, "reg_desc=%.4s\n", (char*)&st->codec->codec_tag); + if (st->codec->codec_id == AV_CODEC_ID_NONE) + mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); + break; + case 0x52: /* stream identifier descriptor */ + st->stream_identifier = 1 + get8(pp, desc_end); + break; + default: + break; + } + *pp = desc_end; + return 0; +} + +static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + PESContext *pes; + AVStream *st; + const uint8_t *p, *p_end, *desc_list_end; + int program_info_length, pcr_pid, pid, stream_type; + int desc_list_len; + uint32_t prog_reg_desc = 0; /* registration descriptor */ + + Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = {{ 0 }}; + int mp4_descr_count = 0; + int i; + + av_dlog(ts->stream, "PMT: len %i\n", section_len); + hex_dump_debug(ts->stream, section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + return; + + av_dlog(ts->stream, "sid=0x%x sec_num=%d/%d\n", + h->id, h->sec_num, h->last_sec_num); + + if (h->tid != PMT_TID) + return; + + clear_program(ts, h->id); + pcr_pid = get16(&p, p_end); + if (pcr_pid < 0) + return; + pcr_pid &= 0x1fff; + add_pid_to_pmt(ts, h->id, pcr_pid); + set_pcr_pid(ts->stream, h->id, pcr_pid); + + av_dlog(ts->stream, "pcr_pid=0x%x\n", pcr_pid); + + program_info_length = get16(&p, p_end); + if (program_info_length < 0) + return; + program_info_length &= 0xfff; + while(program_info_length >= 2) { + uint8_t tag, len; + tag = get8(&p, p_end); + len = get8(&p, p_end); + + av_dlog(ts->stream, "program tag: 0x%02x len=%d\n", tag, len); + + if(len > program_info_length - 2) + //something else is broken, exit the program_descriptors_loop + break; + program_info_length -= len + 2; + if (tag == 0x1d) { // IOD descriptor + get8(&p, p_end); // scope + get8(&p, p_end); // label + len -= 2; + mp4_read_iods(ts->stream, p, len, mp4_descr + mp4_descr_count, + &mp4_descr_count, MAX_MP4_DESCR_COUNT); + } else if (tag == 0x05 && len >= 4) { // registration descriptor + prog_reg_desc = bytestream_get_le32(&p); + len -= 4; + } + p += len; + } + p += program_info_length; + if (p >= p_end) + goto out; + + // stop parsing after pmt, we found header + if (!ts->stream->nb_streams) + ts->stop_parse = 2; + + for(;;) { + st = 0; + pes = NULL; + stream_type = get8(&p, p_end); + if (stream_type < 0) + break; + pid = get16(&p, p_end); + if (pid < 0) + break; + pid &= 0x1fff; + if (pid == ts->current_pid) + break; + + /* now create stream */ + if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { + pes = ts->pids[pid]->u.pes_filter.opaque; + if (!pes->st) { + pes->st = avformat_new_stream(pes->stream, NULL); + if (!pes->st) + goto out; + pes->st->id = pes->pid; + } + st = pes->st; + } else if (stream_type != 0x13) { + if (ts->pids[pid]) mpegts_close_filter(ts, ts->pids[pid]); //wrongly added sdt filter probably + pes = add_pes_stream(ts, pid, pcr_pid); + if (pes) { + st = avformat_new_stream(pes->stream, NULL); + if (!st) + goto out; + st->id = pes->pid; + } + } else { + int idx = ff_find_stream_index(ts->stream, pid); + if (idx >= 0) { + st = ts->stream->streams[idx]; + } else { + st = avformat_new_stream(ts->stream, NULL); + if (!st) + goto out; + st->id = pid; + st->codec->codec_type = AVMEDIA_TYPE_DATA; + } + } + + if (!st) + goto out; + + if (pes && !pes->stream_type) + mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc); + + add_pid_to_pmt(ts, h->id, pid); + + ff_program_add_stream_index(ts->stream, h->id, st->index); + + desc_list_len = get16(&p, p_end); + if (desc_list_len < 0) + break; + desc_list_len &= 0xfff; + desc_list_end = p + desc_list_len; + if (desc_list_end > p_end) + break; + for(;;) { + if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p, desc_list_end, + mp4_descr, mp4_descr_count, pid, ts) < 0) + break; + + if (pes && prog_reg_desc == AV_RL32("HDMV") && stream_type == 0x83 && pes->sub_st) { + ff_program_add_stream_index(ts->stream, h->id, pes->sub_st->index); + pes->sub_st->codec->codec_tag = st->codec->codec_tag; + } + } + p = desc_list_end; + } + + out: + for (i = 0; i < mp4_descr_count; i++) + av_free(mp4_descr[i].dec_config_descr); +} + +static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + const uint8_t *p, *p_end; + int sid, pmt_pid; + AVProgram *program; + + av_dlog(ts->stream, "PAT:\n"); + hex_dump_debug(ts->stream, section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + return; + if (h->tid != PAT_TID) + return; + + ts->stream->ts_id = h->id; + + clear_programs(ts); + for(;;) { + sid = get16(&p, p_end); + if (sid < 0) + break; + pmt_pid = get16(&p, p_end); + if (pmt_pid < 0) + break; + pmt_pid &= 0x1fff; + + if (pmt_pid == ts->current_pid) + break; + + av_dlog(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid); + + if (sid == 0x0000) { + /* NIT info */ + } else { + program = av_new_program(ts->stream, sid); + program->program_num = sid; + program->pmt_pid = pmt_pid; + if (ts->pids[pmt_pid]) + mpegts_close_filter(ts, ts->pids[pmt_pid]); + mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); + add_pat_entry(ts, sid); + add_pid_to_pmt(ts, sid, 0); //add pat pid to program + add_pid_to_pmt(ts, sid, pmt_pid); + } + } + + if (sid < 0) { + int i,j; + for (j=0; jstream->nb_programs; j++) { + for (i=0; inb_prg; i++) + if (ts->prg[i].id == ts->stream->programs[j]->id) + break; + if (i==ts->nb_prg) + clear_avprogram(ts, ts->stream->programs[j]->id); + } + } +} + +static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + const uint8_t *p, *p_end, *desc_list_end, *desc_end; + int onid, val, sid, desc_list_len, desc_tag, desc_len, service_type; + char *name, *provider_name; + + av_dlog(ts->stream, "SDT:\n"); + hex_dump_debug(ts->stream, section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + return; + if (h->tid != SDT_TID) + return; + onid = get16(&p, p_end); + if (onid < 0) + return; + val = get8(&p, p_end); + if (val < 0) + return; + for(;;) { + sid = get16(&p, p_end); + if (sid < 0) + break; + val = get8(&p, p_end); + if (val < 0) + break; + desc_list_len = get16(&p, p_end); + if (desc_list_len < 0) + break; + desc_list_len &= 0xfff; + desc_list_end = p + desc_list_len; + if (desc_list_end > p_end) + break; + for(;;) { + desc_tag = get8(&p, desc_list_end); + if (desc_tag < 0) + break; + desc_len = get8(&p, desc_list_end); + desc_end = p + desc_len; + if (desc_end > desc_list_end) + break; + + av_dlog(ts->stream, "tag: 0x%02x len=%d\n", + desc_tag, desc_len); + + switch(desc_tag) { + case 0x48: + service_type = get8(&p, p_end); + if (service_type < 0) + break; + provider_name = getstr8(&p, p_end); + if (!provider_name) + break; + name = getstr8(&p, p_end); + if (name) { + AVProgram *program = av_new_program(ts->stream, sid); + if(program) { + av_dict_set(&program->metadata, "service_name", name, 0); + av_dict_set(&program->metadata, "service_provider", provider_name, 0); + } + } + av_free(name); + av_free(provider_name); + break; + default: + break; + } + p = desc_end; + } + p = desc_list_end; + } +} + +/* handle one TS packet */ +static int handle_packet(MpegTSContext *ts, const uint8_t *packet) +{ + AVFormatContext *s = ts->stream; + MpegTSFilter *tss; + int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity, + has_adaptation, has_payload; + const uint8_t *p, *p_end; + int64_t pos; + + pid = AV_RB16(packet + 1) & 0x1fff; + if(pid && discard_pid(ts, pid)) + return 0; + is_start = packet[1] & 0x40; + tss = ts->pids[pid]; + if (ts->auto_guess && tss == NULL && is_start) { + add_pes_stream(ts, pid, -1); + tss = ts->pids[pid]; + } + if (!tss) + return 0; + ts->current_pid = pid; + + afc = (packet[3] >> 4) & 3; + if (afc == 0) /* reserved value */ + return 0; + has_adaptation = afc & 2; + has_payload = afc & 1; + is_discontinuity = has_adaptation + && packet[4] != 0 /* with length > 0 */ + && (packet[5] & 0x80); /* and discontinuity indicated */ + + /* continuity check (currently not used) */ + cc = (packet[3] & 0xf); + expected_cc = has_payload ? (tss->last_cc + 1) & 0x0f : tss->last_cc; + cc_ok = pid == 0x1FFF // null packet PID + || is_discontinuity + || tss->last_cc < 0 + || expected_cc == cc; + + tss->last_cc = cc; + if (!cc_ok) { + av_log(ts->stream, AV_LOG_DEBUG, + "Continuity check failed for pid %d expected %d got %d\n", + pid, expected_cc, cc); + if(tss->type == MPEGTS_PES) { + PESContext *pc = tss->u.pes_filter.opaque; + pc->flags |= AV_PKT_FLAG_CORRUPT; + } + } + + if (!has_payload) + return 0; + p = packet + 4; + if (has_adaptation) { + /* skip adaptation field */ + p += p[0] + 1; + } + /* if past the end of packet, ignore */ + p_end = packet + TS_PACKET_SIZE; + if (p >= p_end) + return 0; + + pos = avio_tell(ts->stream->pb); + ts->pos47= pos % ts->raw_packet_size; + + if (tss->type == MPEGTS_SECTION) { + if (is_start) { + /* pointer field present */ + len = *p++; + if (p + len > p_end) + return 0; + if (len && cc_ok) { + /* write remaining section bytes */ + write_section_data(s, tss, + p, len, 0); + /* check whether filter has been closed */ + if (!ts->pids[pid]) + return 0; + } + p += len; + if (p < p_end) { + write_section_data(s, tss, + p, p_end - p, 1); + } + } else { + if (cc_ok) { + write_section_data(s, tss, + p, p_end - p, 0); + } + } + } else { + int ret; + // Note: The position here points actually behind the current packet. + if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, + pos - ts->raw_packet_size)) < 0) + return ret; + } + + return 0; +} + +/* XXX: try to find a better synchro over several packets (use + get_packet_size() ?) */ +static int mpegts_resync(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int c, i; + + for(i = 0;i < MAX_RESYNC_SIZE; i++) { + c = avio_r8(pb); + if (url_feof(pb)) + return -1; + if (c == 0x47) { + avio_seek(pb, -1, SEEK_CUR); + return 0; + } + } + av_log(s, AV_LOG_ERROR, "max resync size reached, could not find sync byte\n"); + /* no sync found */ + return -1; +} + +/* return -1 if error or EOF. Return 0 if OK. */ +static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size) +{ + AVIOContext *pb = s->pb; + int skip, len; + + for(;;) { + len = avio_read(pb, buf, TS_PACKET_SIZE); + if (len != TS_PACKET_SIZE) + return len < 0 ? len : AVERROR_EOF; + /* check packet sync byte */ + if (buf[0] != 0x47) { + /* find a new packet start */ + avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + if (mpegts_resync(s) < 0) + return AVERROR(EAGAIN); + else + continue; + } else { + skip = raw_packet_size - TS_PACKET_SIZE; + if (skip > 0) + avio_skip(pb, skip); + break; + } + } + return 0; +} + +static int handle_packets(MpegTSContext *ts, int nb_packets) +{ + AVFormatContext *s = ts->stream; + uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; + int packet_num, ret = 0; + + if (avio_tell(s->pb) != ts->last_pos) { + int i; + av_dlog(ts->stream, "Skipping after seek\n"); + /* seek detected, flush pes buffer */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i]) { + if (ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + av_buffer_unref(&pes->buffer); + pes->data_index = 0; + pes->state = MPEGTS_SKIP; /* skip until pes header */ + } + ts->pids[i]->last_cc = -1; + } + } + } + + ts->stop_parse = 0; + packet_num = 0; + memset(packet + TS_PACKET_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); + for(;;) { + packet_num++; + if (nb_packets != 0 && packet_num >= nb_packets || + ts->stop_parse > 1) { + ret = AVERROR(EAGAIN); + break; + } + if (ts->stop_parse > 0) + break; + + ret = read_packet(s, packet, ts->raw_packet_size); + if (ret != 0) + break; + ret = handle_packet(ts, packet); + if (ret != 0) + break; + } + ts->last_pos = avio_tell(s->pb); + return ret; +} + +static int mpegts_probe(AVProbeData *p) +{ + const int size= p->buf_size; + int maxscore=0; + int sumscore=0; + int i; + int check_count= size / TS_FEC_PACKET_SIZE; +#define CHECK_COUNT 10 +#define CHECK_BLOCK 100 + + if (check_count < CHECK_COUNT) + return -1; + + for (i=0; ibuf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , NULL); + int dvhs_score= analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, NULL); + int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , NULL); + score = FFMAX3(score, dvhs_score, fec_score); + sumscore += score; + maxscore = FFMAX(maxscore, score); + } + + sumscore = sumscore*CHECK_COUNT/check_count; + maxscore = maxscore*CHECK_COUNT/CHECK_BLOCK; + + av_dlog(0, "TS score: %d %d\n", sumscore, maxscore); + + if (sumscore > 6) return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT; + else if (maxscore > 6) return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT; + else return -1; +} + +/* return the 90kHz PCR and the extension for the 27MHz PCR. return + (-1) if not available */ +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, + const uint8_t *packet) +{ + int afc, len, flags; + const uint8_t *p; + unsigned int v; + + afc = (packet[3] >> 4) & 3; + if (afc <= 1) + return -1; + p = packet + 4; + len = p[0]; + p++; + if (len == 0) + return -1; + flags = *p++; + len--; + if (!(flags & 0x10)) + return -1; + if (len < 6) + return -1; + v = AV_RB32(p); + *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7); + *ppcr_low = ((p[4] & 1) << 8) | p[5]; + return 0; +} + +static int mpegts_read_header(AVFormatContext *s) +{ + MpegTSContext *ts = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t buf[8*1024]={0}; + int len; + int64_t pos; + + /* read the first 8192 bytes to get packet size */ + pos = avio_tell(pb); + len = avio_read(pb, buf, sizeof(buf)); + ts->raw_packet_size = get_packet_size(buf, len); + if (ts->raw_packet_size <= 0) { + av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n"); + ts->raw_packet_size = TS_PACKET_SIZE; + } + ts->stream = s; + ts->auto_guess = 0; + + if (s->iformat == &ff_mpegts_demuxer) { + /* normal demux */ + + /* first do a scan to get all the services */ + /* NOTE: We attempt to seek on non-seekable files as well, as the + * probe buffer usually is big enough. Only warn if the seek failed + * on files where the seek should work. */ + if (avio_seek(pb, pos, SEEK_SET) < 0) + av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n"); + + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + + handle_packets(ts, s->probesize / ts->raw_packet_size); + /* if could not find service, enable auto_guess */ + + ts->auto_guess = 1; + + av_dlog(ts->stream, "tuning done\n"); + + s->ctx_flags |= AVFMTCTX_NOHEADER; + } else { + AVStream *st; + int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l; + int64_t pcrs[2], pcr_h; + int packet_count[2]; + uint8_t packet[TS_PACKET_SIZE]; + + /* only read packets */ + + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + avpriv_set_pts_info(st, 60, 1, 27000000); + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_MPEG2TS; + + /* we iterate until we find two PCRs to estimate the bitrate */ + pcr_pid = -1; + nb_pcrs = 0; + nb_packets = 0; + for(;;) { + ret = read_packet(s, packet, ts->raw_packet_size); + if (ret < 0) + return -1; + pid = AV_RB16(packet + 1) & 0x1fff; + if ((pcr_pid == -1 || pcr_pid == pid) && + parse_pcr(&pcr_h, &pcr_l, packet) == 0) { + pcr_pid = pid; + packet_count[nb_pcrs] = nb_packets; + pcrs[nb_pcrs] = pcr_h * 300 + pcr_l; + nb_pcrs++; + if (nb_pcrs >= 2) + break; + } + nb_packets++; + } + + /* NOTE1: the bitrate is computed without the FEC */ + /* NOTE2: it is only the bitrate of the start of the stream */ + ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]); + ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0]; + s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr; + st->codec->bit_rate = s->bit_rate; + st->start_time = ts->cur_pcr; + av_dlog(ts->stream, "start=%0.3f pcr=%0.3f incr=%d\n", + st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr); + } + + avio_seek(pb, pos, SEEK_SET); + return 0; + fail: + return -1; +} + +#define MAX_PACKET_READAHEAD ((128 * 1024) / 188) + +static int mpegts_raw_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegTSContext *ts = s->priv_data; + int ret, i; + int64_t pcr_h, next_pcr_h, pos; + int pcr_l, next_pcr_l; + uint8_t pcr_buf[12]; + + if (av_new_packet(pkt, TS_PACKET_SIZE) < 0) + return AVERROR(ENOMEM); + pkt->pos= avio_tell(s->pb); + ret = read_packet(s, pkt->data, ts->raw_packet_size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + if (ts->mpeg2ts_compute_pcr) { + /* compute exact PCR for each packet */ + if (parse_pcr(&pcr_h, &pcr_l, pkt->data) == 0) { + /* we read the next PCR (XXX: optimize it by using a bigger buffer */ + pos = avio_tell(s->pb); + for(i = 0; i < MAX_PACKET_READAHEAD; i++) { + avio_seek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET); + avio_read(s->pb, pcr_buf, 12); + if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) { + /* XXX: not precise enough */ + ts->pcr_incr = ((next_pcr_h - pcr_h) * 300 + (next_pcr_l - pcr_l)) / + (i + 1); + break; + } + } + avio_seek(s->pb, pos, SEEK_SET); + /* no next PCR found: we use previous increment */ + ts->cur_pcr = pcr_h * 300 + pcr_l; + } + pkt->pts = ts->cur_pcr; + pkt->duration = ts->pcr_incr; + ts->cur_pcr += ts->pcr_incr; + } + pkt->stream_index = 0; + return 0; +} + +static int mpegts_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegTSContext *ts = s->priv_data; + int ret, i; + + pkt->size = -1; + ts->pkt = pkt; + ret = handle_packets(ts, 0); + if (ret < 0) { + av_free_packet(ts->pkt); + /* flush pes data left */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, pkt); + pes->state = MPEGTS_SKIP; + ret = 0; + break; + } + } + } + } + + if (!ret && pkt->size < 0) + ret = AVERROR(EINTR); + return ret; +} + +static void mpegts_free(MpegTSContext *ts) +{ + int i; + + clear_programs(ts); + + for(i=0;ipids[i]) mpegts_close_filter(ts, ts->pids[i]); +} + +static int mpegts_read_close(AVFormatContext *s) +{ + MpegTSContext *ts = s->priv_data; + mpegts_free(ts); + return 0; +} + +static av_unused int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + MpegTSContext *ts = s->priv_data; + int64_t pos, timestamp; + uint8_t buf[TS_PACKET_SIZE]; + int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; + while(pos < pos_limit) { + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + return AV_NOPTS_VALUE; + if (buf[0] != 0x47) { + if (mpegts_resync(s) < 0) + return AV_NOPTS_VALUE; + pos = avio_tell(s->pb); + continue; + } + if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && + parse_pcr(×tamp, &pcr_l, buf) == 0) { + *ppos = pos; + return timestamp; + } + pos += ts->raw_packet_size; + } + + return AV_NOPTS_VALUE; +} + +static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + MpegTSContext *ts = s->priv_data; + int64_t pos; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; + ff_read_frame_flush(s); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + while(pos < pos_limit) { + int ret; + AVPacket pkt; + av_init_packet(&pkt); + ret= av_read_frame(s, &pkt); + if(ret < 0) + return AV_NOPTS_VALUE; + av_free_packet(&pkt); + if(pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0){ + ff_reduce_index(s, pkt.stream_index); + av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + if(pkt.stream_index == stream_index){ + *ppos= pkt.pos; + return pkt.dts; + } + } + pos = pkt.pos; + } + + return AV_NOPTS_VALUE; +} + +/**************************************************************/ +/* parsing functions - called from other demuxers such as RTP */ + +MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) +{ + MpegTSContext *ts; + + ts = av_mallocz(sizeof(MpegTSContext)); + if (!ts) + return NULL; + /* no stream case, currently used by RTP */ + ts->raw_packet_size = TS_PACKET_SIZE; + ts->stream = s; + ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + + return ts; +} + +/* return the consumed length if a packet was output, or -1 if no + packet is output */ +int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len) +{ + int len1; + + len1 = len; + ts->pkt = pkt; + for(;;) { + ts->stop_parse = 0; + if (len < TS_PACKET_SIZE) + return -1; + if (buf[0] != 0x47) { + buf++; + len--; + } else { + handle_packet(ts, buf); + buf += TS_PACKET_SIZE; + len -= TS_PACKET_SIZE; + if (ts->stop_parse == 1) + break; + } + } + return len1 - len; +} + +void ff_mpegts_parse_close(MpegTSContext *ts) +{ + mpegts_free(ts); + av_free(ts); +} + +AVInputFormat ff_mpegts_demuxer = { + .name = "mpegts", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), + .priv_data_size = sizeof(MpegTSContext), + .read_probe = mpegts_probe, + .read_header = mpegts_read_header, + .read_packet = mpegts_read_packet, + .read_close = mpegts_read_close, + .read_timestamp = mpegts_get_dts, + .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, +}; + +AVInputFormat ff_mpegtsraw_demuxer = { + .name = "mpegtsraw", + .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"), + .priv_data_size = sizeof(MpegTSContext), + .read_header = mpegts_read_header, + .read_packet = mpegts_raw_read_packet, + .read_close = mpegts_read_close, + .read_timestamp = mpegts_get_dts, + .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, + .priv_class = &mpegtsraw_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegts.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegts.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/mpegts.o libavformat/mpegts.o: libavformat/mpegts.c \ + libavutil/buffer.h libavutil/crc.h libavutil/attributes.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/bswap.h \ + config.h libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavutil/log.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/dict.h \ + libavutil/mathematics.h libavutil/opt.h libavutil/samplefmt.h \ + libavutil/avassert.h libavcodec/bytestream.h libavutil/common.h \ + libavutil/intreadwrite.h libavcodec/get_bits.h libavutil/log.h \ + libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/mpegts.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/seek.h \ + libavformat/mpeg.h libavformat/isom.h libavformat/dv.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegts.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,107 @@ +/* + * MPEG2 transport stream defines + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_MPEGTS_H +#define AVFORMAT_MPEGTS_H + +#include "avformat.h" + +#define TS_FEC_PACKET_SIZE 204 +#define TS_DVHS_PACKET_SIZE 192 +#define TS_PACKET_SIZE 188 +#define TS_MAX_PACKET_SIZE 204 + +#define NB_PID_MAX 8192 +#define MAX_SECTION_SIZE 4096 + +/* pids */ +#define PAT_PID 0x0000 +#define SDT_PID 0x0011 + +/* table ids */ +#define PAT_TID 0x00 +#define PMT_TID 0x02 +#define M4OD_TID 0x05 +#define SDT_TID 0x42 + +#define STREAM_TYPE_VIDEO_MPEG1 0x01 +#define STREAM_TYPE_VIDEO_MPEG2 0x02 +#define STREAM_TYPE_AUDIO_MPEG1 0x03 +#define STREAM_TYPE_AUDIO_MPEG2 0x04 +#define STREAM_TYPE_PRIVATE_SECTION 0x05 +#define STREAM_TYPE_PRIVATE_DATA 0x06 +#define STREAM_TYPE_AUDIO_AAC 0x0f +#define STREAM_TYPE_AUDIO_AAC_LATM 0x11 +#define STREAM_TYPE_VIDEO_MPEG4 0x10 +#define STREAM_TYPE_VIDEO_H264 0x1b +#define STREAM_TYPE_VIDEO_CAVS 0x42 +#define STREAM_TYPE_VIDEO_VC1 0xea +#define STREAM_TYPE_VIDEO_DIRAC 0xd1 + +#define STREAM_TYPE_AUDIO_AC3 0x81 +#define STREAM_TYPE_AUDIO_DTS 0x8a + +typedef struct MpegTSContext MpegTSContext; + +MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s); +int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len); +void ff_mpegts_parse_close(MpegTSContext *ts); + +typedef struct SLConfigDescr { + int use_au_start; + int use_au_end; + int use_rand_acc_pt; + int use_padding; + int use_timestamps; + int use_idle; + int timestamp_res; + int timestamp_len; + int ocr_len; + int au_len; + int inst_bitrate_len; + int degr_prior_len; + int au_seq_num_len; + int packet_seq_num_len; +} SLConfigDescr; + +typedef struct Mp4Descr { + int es_id; + int dec_config_descr_len; + uint8_t *dec_config_descr; + SLConfigDescr sl; +} Mp4Descr; + +/** + * Parse an MPEG-2 descriptor + * @param[in] fc Format context (used for logging only) + * @param st Stream + * @param stream_type STREAM_TYPE_xxx + * @param pp Descriptor buffer pointer + * @param desc_list_end End of buffer + * @return <0 to stop processing + */ +int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, + const uint8_t **pp, const uint8_t *desc_list_end, + Mp4Descr *mp4_descr, int mp4_descr_count, int pid, + MpegTSContext *ts); + +#endif /* AVFORMAT_MPEGTS_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegts.o Binary file ffmpeg/libavformat/mpegts.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegtsenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegtsenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1272 @@ +/* + * MPEG2 transport stream (aka DVB) muxer + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/bswap.h" +#include "libavutil/crc.h" +#include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "libavcodec/internal.h" +#include "avformat.h" +#include "internal.h" +#include "mpegts.h" + +#define PCR_TIME_BASE 27000000 + +/* write DVB SI sections */ + +/*********************************************/ +/* mpegts section writer */ + +typedef struct MpegTSSection { + int pid; + int cc; + void (*write_packet)(struct MpegTSSection *s, const uint8_t *packet); + void *opaque; +} MpegTSSection; + +typedef struct MpegTSService { + MpegTSSection pmt; /* MPEG2 pmt table context */ + int sid; /* service ID */ + char *name; + char *provider_name; + int pcr_pid; + int pcr_packet_count; + int pcr_packet_period; +} MpegTSService; + +typedef struct MpegTSWrite { + const AVClass *av_class; + MpegTSSection pat; /* MPEG2 pat table */ + MpegTSSection sdt; /* MPEG2 sdt table context */ + MpegTSService **services; + int sdt_packet_count; + int sdt_packet_period; + int pat_packet_count; + int pat_packet_period; + int nb_services; + int onid; + int tsid; + int64_t first_pcr; + int mux_rate; ///< set to 1 when VBR + int pes_payload_size; + + int transport_stream_id; + int original_network_id; + int service_id; + + int pmt_start_pid; + int start_pid; + int m2ts_mode; + + int reemit_pat_pmt; // backward compatibility + +#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 +#define MPEGTS_FLAG_AAC_LATM 0x02 + int flags; + int copyts; +} MpegTSWrite; + +/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ +#define DEFAULT_PES_HEADER_FREQ 16 +#define DEFAULT_PES_PAYLOAD_SIZE ((DEFAULT_PES_HEADER_FREQ - 1) * 184 + 170) + +static const AVOption options[] = { + { "mpegts_transport_stream_id", "Set transport_stream_id field.", + offsetof(MpegTSWrite, transport_stream_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_original_network_id", "Set original_network_id field.", + offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_service_id", "Set service_id field.", + offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", + offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_start_pid", "Set the first pid.", + offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.i64 = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM}, + {"mpegts_m2ts_mode", "Enable m2ts mode.", + offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, + -1,1, AV_OPT_FLAG_ENCODING_PARAM}, + { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "pes_payload_size", "Minimum PES packet payload in bytes", + offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT_PES_PAYLOAD_SIZE}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_flags", "MPEG-TS muxing flags", offsetof(MpegTSWrite, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + { "resend_headers", "Reemit PAT/PMT before writing the next packet", + 0, AV_OPT_TYPE_CONST, {.i64 = MPEGTS_FLAG_REEMIT_PAT_PMT}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags"}, + { "latm", "Use LATM packetization for AAC", + 0, AV_OPT_TYPE_CONST, {.i64 = MPEGTS_FLAG_AAC_LATM}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags"}, + // backward compatibility + { "resend_headers", "Reemit PAT/PMT before writing the next packet", + offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "mpegts_copyts", "dont offset dts/pts", + offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +static const AVClass mpegts_muxer_class = { + .class_name = "MPEGTS muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* NOTE: 4 bytes must be left at the end for the crc32 */ +static void mpegts_write_section(MpegTSSection *s, uint8_t *buf, int len) +{ + unsigned int crc; + unsigned char packet[TS_PACKET_SIZE]; + const unsigned char *buf_ptr; + unsigned char *q; + int first, b, len1, left; + + crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, buf, len - 4)); + buf[len - 4] = (crc >> 24) & 0xff; + buf[len - 3] = (crc >> 16) & 0xff; + buf[len - 2] = (crc >> 8) & 0xff; + buf[len - 1] = (crc) & 0xff; + + /* send each packet */ + buf_ptr = buf; + while (len > 0) { + first = (buf == buf_ptr); + q = packet; + *q++ = 0x47; + b = (s->pid >> 8); + if (first) + b |= 0x40; + *q++ = b; + *q++ = s->pid; + s->cc = (s->cc + 1) & 0xf; + *q++ = 0x10 | s->cc; + if (first) + *q++ = 0; /* 0 offset */ + len1 = TS_PACKET_SIZE - (q - packet); + if (len1 > len) + len1 = len; + memcpy(q, buf_ptr, len1); + q += len1; + /* add known padding data */ + left = TS_PACKET_SIZE - (q - packet); + if (left > 0) + memset(q, 0xff, left); + + s->write_packet(s, packet); + + buf_ptr += len1; + len -= len1; + } +} + +static inline void put16(uint8_t **q_ptr, int val) +{ + uint8_t *q; + q = *q_ptr; + *q++ = val >> 8; + *q++ = val; + *q_ptr = q; +} + +static int mpegts_write_section1(MpegTSSection *s, int tid, int id, + int version, int sec_num, int last_sec_num, + uint8_t *buf, int len) +{ + uint8_t section[1024], *q; + unsigned int tot_len; + /* reserved_future_use field must be set to 1 for SDT */ + unsigned int flags = tid == SDT_TID ? 0xf000 : 0xb000; + + tot_len = 3 + 5 + len + 4; + /* check if not too big */ + if (tot_len > 1024) + return AVERROR_INVALIDDATA; + + q = section; + *q++ = tid; + put16(&q, flags | (len + 5 + 4)); /* 5 byte header + 4 byte CRC */ + put16(&q, id); + *q++ = 0xc1 | (version << 1); /* current_next_indicator = 1 */ + *q++ = sec_num; + *q++ = last_sec_num; + memcpy(q, buf, len); + + mpegts_write_section(s, section, tot_len); + return 0; +} + +/*********************************************/ +/* mpegts writer */ + +#define DEFAULT_PROVIDER_NAME "FFmpeg" +#define DEFAULT_SERVICE_NAME "Service01" + +/* we retransmit the SI info at this rate */ +#define SDT_RETRANS_TIME 500 +#define PAT_RETRANS_TIME 100 +#define PCR_RETRANS_TIME 20 + +typedef struct MpegTSWriteStream { + struct MpegTSService *service; + int pid; /* stream associated pid */ + int cc; + int payload_size; + int first_pts_check; ///< first pts check needed + int prev_payload_key; + int64_t payload_pts; + int64_t payload_dts; + int payload_flags; + uint8_t *payload; + AVFormatContext *amux; +} MpegTSWriteStream; + +static void mpegts_write_pat(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSService *service; + uint8_t data[1012], *q; + int i; + + q = data; + for(i = 0; i < ts->nb_services; i++) { + service = ts->services[i]; + put16(&q, service->sid); + put16(&q, 0xe000 | service->pmt.pid); + } + mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, 0, 0, 0, + data, q - data); +} + +static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) +{ + MpegTSWrite *ts = s->priv_data; + uint8_t data[1012], *q, *desc_length_ptr, *program_info_length_ptr; + int val, stream_type, i; + + q = data; + put16(&q, 0xe000 | service->pcr_pid); + + program_info_length_ptr = q; + q += 2; /* patched after */ + + /* put program info here */ + + val = 0xf000 | (q - program_info_length_ptr - 2); + program_info_length_ptr[0] = val >> 8; + program_info_length_ptr[1] = val; + + for(i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MpegTSWriteStream *ts_st = st->priv_data; + AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0); + switch(st->codec->codec_id) { + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + stream_type = STREAM_TYPE_VIDEO_MPEG2; + break; + case AV_CODEC_ID_MPEG4: + stream_type = STREAM_TYPE_VIDEO_MPEG4; + break; + case AV_CODEC_ID_H264: + stream_type = STREAM_TYPE_VIDEO_H264; + break; + case AV_CODEC_ID_CAVS: + stream_type = STREAM_TYPE_VIDEO_CAVS; + break; + case AV_CODEC_ID_DIRAC: + stream_type = STREAM_TYPE_VIDEO_DIRAC; + break; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + stream_type = STREAM_TYPE_AUDIO_MPEG1; + break; + case AV_CODEC_ID_AAC: + stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) ? STREAM_TYPE_AUDIO_AAC_LATM : STREAM_TYPE_AUDIO_AAC; + break; + case AV_CODEC_ID_AAC_LATM: + stream_type = STREAM_TYPE_AUDIO_AAC_LATM; + break; + case AV_CODEC_ID_AC3: + stream_type = STREAM_TYPE_AUDIO_AC3; + break; + default: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + } + *q++ = stream_type; + put16(&q, 0xe000 | ts_st->pid); + desc_length_ptr = q; + q += 2; /* patched after */ + + /* write optional descriptors here */ + switch(st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if(st->codec->codec_id==AV_CODEC_ID_EAC3){ + *q++=0x7a; // EAC3 descriptor see A038 DVB SI + *q++=1; // 1 byte, all flags sets to 0 + *q++=0; // omit all fields... + } + if(st->codec->codec_id==AV_CODEC_ID_S302M){ + *q++ = 0x05; /* MPEG-2 registration descriptor*/ + *q++ = 4; + *q++ = 'B'; + *q++ = 'S'; + *q++ = 'S'; + *q++ = 'D'; + } + + if (lang) { + char *p; + char *next = lang->value; + uint8_t *len_ptr; + + *q++ = 0x0a; /* ISO 639 language descriptor */ + len_ptr = q++; + *len_ptr = 0; + + for (p = lang->value; next && *len_ptr < 255 / 4 * 4; p = next + 1) { + next = strchr(p, ','); + if (strlen(p) != 3 && (!next || next != p + 3)) + continue; /* not a 3-letter code */ + + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + + if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS) + *q++ = 0x01; + else if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) + *q++ = 0x02; + else if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED) + *q++ = 0x03; + else + *q++ = 0; /* undefined type */ + + *len_ptr += 4; + } + + if (*len_ptr == 0) + q -= 2; /* no language codes were written */ + } + break; + case AVMEDIA_TYPE_SUBTITLE: + { + const char *language; + language = lang && strlen(lang->value)==3 ? lang->value : "eng"; + *q++ = 0x59; + *q++ = 8; + *q++ = language[0]; + *q++ = language[1]; + *q++ = language[2]; + *q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */ + if(st->codec->extradata_size == 4) { + memcpy(q, st->codec->extradata, 4); + q += 4; + } else { + put16(&q, 1); /* page id */ + put16(&q, 1); /* ancillary page id */ + } + } + break; + case AVMEDIA_TYPE_VIDEO: + if (stream_type == STREAM_TYPE_VIDEO_DIRAC) { + *q++ = 0x05; /*MPEG-2 registration descriptor*/ + *q++ = 4; + *q++ = 'd'; + *q++ = 'r'; + *q++ = 'a'; + *q++ = 'c'; + } + break; + } + + val = 0xf000 | (q - desc_length_ptr - 2); + desc_length_ptr[0] = val >> 8; + desc_length_ptr[1] = val; + } + mpegts_write_section1(&service->pmt, PMT_TID, service->sid, 0, 0, 0, + data, q - data); +} + +/* NOTE: str == NULL is accepted for an empty string */ +static void putstr8(uint8_t **q_ptr, const char *str) +{ + uint8_t *q; + int len; + + q = *q_ptr; + if (!str) + len = 0; + else + len = strlen(str); + *q++ = len; + memcpy(q, str, len); + q += len; + *q_ptr = q; +} + +static void mpegts_write_sdt(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSService *service; + uint8_t data[1012], *q, *desc_list_len_ptr, *desc_len_ptr; + int i, running_status, free_ca_mode, val; + + q = data; + put16(&q, ts->onid); + *q++ = 0xff; + for(i = 0; i < ts->nb_services; i++) { + service = ts->services[i]; + put16(&q, service->sid); + *q++ = 0xfc | 0x00; /* currently no EIT info */ + desc_list_len_ptr = q; + q += 2; + running_status = 4; /* running */ + free_ca_mode = 0; + + /* write only one descriptor for the service name and provider */ + *q++ = 0x48; + desc_len_ptr = q; + q++; + *q++ = 0x01; /* digital television service */ + putstr8(&q, service->provider_name); + putstr8(&q, service->name); + desc_len_ptr[0] = q - desc_len_ptr - 1; + + /* fill descriptor length */ + val = (running_status << 13) | (free_ca_mode << 12) | + (q - desc_list_len_ptr - 2); + desc_list_len_ptr[0] = val >> 8; + desc_list_len_ptr[1] = val; + } + mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, 0, 0, 0, + data, q - data); +} + +static MpegTSService *mpegts_add_service(MpegTSWrite *ts, + int sid, + const char *provider_name, + const char *name) +{ + MpegTSService *service; + + service = av_mallocz(sizeof(MpegTSService)); + if (!service) + return NULL; + service->pmt.pid = ts->pmt_start_pid + ts->nb_services; + service->sid = sid; + service->provider_name = av_strdup(provider_name); + service->name = av_strdup(name); + service->pcr_pid = 0x1fff; + dynarray_add(&ts->services, &ts->nb_services, service); + return service; +} + +static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) +{ + return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + + ts->first_pcr; +} + +static void mpegts_prefix_m2ts_header(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + if (ts->m2ts_mode) { + int64_t pcr = get_pcr(s->priv_data, s->pb); + uint32_t tp_extra_header = pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); + } +} + +static void section_write_packet(MpegTSSection *s, const uint8_t *packet) +{ + AVFormatContext *ctx = s->opaque; + mpegts_prefix_m2ts_header(ctx); + avio_write(ctx->pb, packet, TS_PACKET_SIZE); +} + +static int mpegts_write_header(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st; + MpegTSService *service; + AVStream *st, *pcr_st = NULL; + AVDictionaryEntry *title, *provider; + int i, j; + const char *service_name; + const char *provider_name; + int *pids; + int ret; + + if (s->max_delay < 0) /* Not set by the caller */ + s->max_delay = 0; + + // round up to a whole number of TS packets + ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14; + + ts->tsid = ts->transport_stream_id; + ts->onid = ts->original_network_id; + /* allocate a single DVB service */ + title = av_dict_get(s->metadata, "service_name", NULL, 0); + if (!title) + title = av_dict_get(s->metadata, "title", NULL, 0); + service_name = title ? title->value : DEFAULT_SERVICE_NAME; + provider = av_dict_get(s->metadata, "service_provider", NULL, 0); + provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME; + service = mpegts_add_service(ts, ts->service_id, provider_name, service_name); + service->pmt.write_packet = section_write_packet; + service->pmt.opaque = s; + service->pmt.cc = 15; + + ts->pat.pid = PAT_PID; + ts->pat.cc = 15; // Initialize at 15 so that it wraps and be equal to 0 for the first packet we write + ts->pat.write_packet = section_write_packet; + ts->pat.opaque = s; + + ts->sdt.pid = SDT_PID; + ts->sdt.cc = 15; + ts->sdt.write_packet = section_write_packet; + ts->sdt.opaque = s; + + pids = av_malloc(s->nb_streams * sizeof(*pids)); + if (!pids) + return AVERROR(ENOMEM); + + /* assign pids to each stream */ + for(i = 0;i < s->nb_streams; i++) { + st = s->streams[i]; + avpriv_set_pts_info(st, 33, 1, 90000); + ts_st = av_mallocz(sizeof(MpegTSWriteStream)); + if (!ts_st) { + ret = AVERROR(ENOMEM); + goto fail; + } + st->priv_data = ts_st; + ts_st->payload = av_mallocz(ts->pes_payload_size); + if (!ts_st->payload) { + ret = AVERROR(ENOMEM); + goto fail; + } + ts_st->service = service; + /* MPEG pid values < 16 are reserved. Applications which set st->id in + * this range are assigned a calculated pid. */ + if (st->id < 16) { + ts_st->pid = ts->start_pid + i; + } else if (st->id < 0x1FFF) { + ts_st->pid = st->id; + } else { + av_log(s, AV_LOG_ERROR, "Invalid stream id %d, must be less than 8191\n", st->id); + ret = AVERROR(EINVAL); + goto fail; + } + if (ts_st->pid == service->pmt.pid) { + av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); + ret = AVERROR(EINVAL); + goto fail; + } + for (j = 0; j < i; j++) + if (pids[j] == ts_st->pid) { + av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); + ret = AVERROR(EINVAL); + goto fail; + } + pids[i] = ts_st->pid; + ts_st->payload_pts = AV_NOPTS_VALUE; + ts_st->payload_dts = AV_NOPTS_VALUE; + ts_st->first_pts_check = 1; + ts_st->cc = 15; + /* update PCR pid by using the first video stream */ + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + service->pcr_pid == 0x1fff) { + service->pcr_pid = ts_st->pid; + pcr_st = st; + } + if (st->codec->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0) + { + AVStream *ast; + ts_st->amux = avformat_alloc_context(); + if (!ts_st->amux) { + ret = AVERROR(ENOMEM); + goto fail; + } + ts_st->amux->oformat = av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts", NULL, NULL); + if (!ts_st->amux->oformat) { + ret = AVERROR(EINVAL); + goto fail; + } + ast = avformat_new_stream(ts_st->amux, NULL); + ret = avcodec_copy_context(ast->codec, st->codec); + if (ret != 0) + goto fail; + ret = avformat_write_header(ts_st->amux, NULL); + if (ret < 0) + goto fail; + } + } + + av_free(pids); + + /* if no video stream, use the first stream as PCR */ + if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { + pcr_st = s->streams[0]; + ts_st = pcr_st->priv_data; + service->pcr_pid = ts_st->pid; + } + + if (ts->mux_rate > 1) { + service->pcr_packet_period = (ts->mux_rate * PCR_RETRANS_TIME) / + (TS_PACKET_SIZE * 8 * 1000); + ts->sdt_packet_period = (ts->mux_rate * SDT_RETRANS_TIME) / + (TS_PACKET_SIZE * 8 * 1000); + ts->pat_packet_period = (ts->mux_rate * PAT_RETRANS_TIME) / + (TS_PACKET_SIZE * 8 * 1000); + + if(ts->copyts < 1) + ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); + } else { + /* Arbitrary values, PAT/PMT will also be written on video key frames */ + ts->sdt_packet_period = 200; + ts->pat_packet_period = 40; + if (pcr_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (!pcr_st->codec->frame_size) { + av_log(s, AV_LOG_WARNING, "frame size not set\n"); + service->pcr_packet_period = + pcr_st->codec->sample_rate/(10*512); + } else { + service->pcr_packet_period = + pcr_st->codec->sample_rate/(10*pcr_st->codec->frame_size); + } + } else { + // max delta PCR 0.1s + service->pcr_packet_period = + pcr_st->codec->time_base.den/(10*pcr_st->codec->time_base.num); + } + if(!service->pcr_packet_period) + service->pcr_packet_period = 1; + } + + // output a PCR as soon as possible + service->pcr_packet_count = service->pcr_packet_period; + ts->pat_packet_count = ts->pat_packet_period-1; + ts->sdt_packet_count = ts->sdt_packet_period-1; + + if (ts->mux_rate == 1) + av_log(s, AV_LOG_VERBOSE, "muxrate VBR, "); + else + av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate); + av_log(s, AV_LOG_VERBOSE, "pcr every %d pkts, " + "sdt every %d, pat/pmt every %d pkts\n", + service->pcr_packet_period, + ts->sdt_packet_period, ts->pat_packet_period); + + if (ts->m2ts_mode == -1) { + if (av_match_ext(s->filename, "m2ts")) { + ts->m2ts_mode = 1; + } else { + ts->m2ts_mode = 0; + } + } + + avio_flush(s->pb); + + return 0; + + fail: + av_free(pids); + for(i = 0;i < s->nb_streams; i++) { + MpegTSWriteStream *ts_st; + st = s->streams[i]; + ts_st = st->priv_data; + if (ts_st) { + av_freep(&ts_st->payload); + if (ts_st->amux) { + avformat_free_context(ts_st->amux); + ts_st->amux = NULL; + } + } + av_freep(&st->priv_data); + } + return ret; +} + +/* send SDT, PAT and PMT tables regulary */ +static void retransmit_si_info(AVFormatContext *s, int force_pat) +{ + MpegTSWrite *ts = s->priv_data; + int i; + + if (++ts->sdt_packet_count == ts->sdt_packet_period) { + ts->sdt_packet_count = 0; + mpegts_write_sdt(s); + } + if (++ts->pat_packet_count == ts->pat_packet_period || force_pat) { + ts->pat_packet_count = 0; + mpegts_write_pat(s); + for(i = 0; i < ts->nb_services; i++) { + mpegts_write_pmt(s, ts->services[i]); + } + } +} + +static int write_pcr_bits(uint8_t *buf, int64_t pcr) +{ + int64_t pcr_low = pcr % 300, pcr_high = pcr / 300; + + *buf++ = pcr_high >> 25; + *buf++ = pcr_high >> 17; + *buf++ = pcr_high >> 9; + *buf++ = pcr_high >> 1; + *buf++ = pcr_high << 7 | pcr_low >> 8 | 0x7e; + *buf++ = pcr_low; + + return 6; +} + +/* Write a single null transport stream packet */ +static void mpegts_insert_null_packet(AVFormatContext *s) +{ + uint8_t *q; + uint8_t buf[TS_PACKET_SIZE]; + + q = buf; + *q++ = 0x47; + *q++ = 0x00 | 0x1f; + *q++ = 0xff; + *q++ = 0x10; + memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf)); + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); +} + +/* Write a single transport stream packet with a PCR and no payload */ +static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st = st->priv_data; + uint8_t *q; + uint8_t buf[TS_PACKET_SIZE]; + + q = buf; + *q++ = 0x47; + *q++ = ts_st->pid >> 8; + *q++ = ts_st->pid; + *q++ = 0x20 | ts_st->cc; /* Adaptation only */ + /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */ + *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */ + *q++ = 0x10; /* Adaptation flags: PCR present */ + + /* PCR coded into 6 bytes */ + q += write_pcr_bits(q, get_pcr(ts, s->pb)); + + /* stuffing bytes */ + memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); +} + +static void write_pts(uint8_t *q, int fourbits, int64_t pts) +{ + int val; + + val = fourbits << 4 | (((pts >> 30) & 0x07) << 1) | 1; + *q++ = val; + val = (((pts >> 15) & 0x7fff) << 1) | 1; + *q++ = val >> 8; + *q++ = val; + val = (((pts) & 0x7fff) << 1) | 1; + *q++ = val >> 8; + *q++ = val; +} + +/* Set an adaptation field flag in an MPEG-TS packet*/ +static void set_af_flag(uint8_t *pkt, int flag) +{ + // expect at least one flag to set + av_assert0(flag); + + if ((pkt[3] & 0x20) == 0) { + // no AF yet, set adaptation field flag + pkt[3] |= 0x20; + // 1 byte length, no flags + pkt[4] = 1; + pkt[5] = 0; + } + pkt[5] |= flag; +} + +/* Extend the adaptation field by size bytes */ +static void extend_af(uint8_t *pkt, int size) +{ + // expect already existing adaptation field + av_assert0(pkt[3] & 0x20); + pkt[4] += size; +} + +/* Get a pointer to MPEG-TS payload (right after TS packet header) */ +static uint8_t *get_ts_payload_start(uint8_t *pkt) +{ + if (pkt[3] & 0x20) + return pkt + 5 + pkt[4]; + else + return pkt + 4; +} + +/* Add a pes header to the front of payload, and segment into an integer number of + * ts packets. The final ts packet is padded using an over-sized adaptation header + * to exactly fill the last ts packet. + * NOTE: 'payload' contains a complete PES payload. + */ +static void mpegts_write_pes(AVFormatContext *s, AVStream *st, + const uint8_t *payload, int payload_size, + int64_t pts, int64_t dts, int key) +{ + MpegTSWriteStream *ts_st = st->priv_data; + MpegTSWrite *ts = s->priv_data; + uint8_t buf[TS_PACKET_SIZE]; + uint8_t *q; + int val, is_start, len, header_len, write_pcr, private_code, flags; + int afc_len, stuffing_len; + int64_t pcr = -1; /* avoid warning */ + int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); + int force_pat = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; + + is_start = 1; + while (payload_size > 0) { + retransmit_si_info(s, force_pat); + force_pat = 0; + + write_pcr = 0; + if (ts_st->pid == ts_st->service->pcr_pid) { + if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames + ts_st->service->pcr_packet_count++; + if (ts_st->service->pcr_packet_count >= + ts_st->service->pcr_packet_period) { + ts_st->service->pcr_packet_count = 0; + write_pcr = 1; + } + } + + if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE && + (dts - get_pcr(ts, s->pb)/300) > delay) { + /* pcr insert gets priority over null packet insert */ + if (write_pcr) + mpegts_insert_pcr_only(s, st); + else + mpegts_insert_null_packet(s); + continue; /* recalculate write_pcr and possibly retransmit si_info */ + } + + /* prepare packet header */ + q = buf; + *q++ = 0x47; + val = (ts_st->pid >> 8); + if (is_start) + val |= 0x40; + *q++ = val; + *q++ = ts_st->pid; + ts_st->cc = (ts_st->cc + 1) & 0xf; + *q++ = 0x10 | ts_st->cc; // payload indicator + CC + if (key && is_start && pts != AV_NOPTS_VALUE) { + // set Random Access for key frames + if (ts_st->pid == ts_st->service->pcr_pid) + write_pcr = 1; + set_af_flag(buf, 0x40); + q = get_ts_payload_start(buf); + } + if (write_pcr) { + set_af_flag(buf, 0x10); + q = get_ts_payload_start(buf); + // add 11, pcr references the last byte of program clock reference base + if (ts->mux_rate > 1) + pcr = get_pcr(ts, s->pb); + else + pcr = (dts - delay)*300; + if (dts != AV_NOPTS_VALUE && dts < pcr / 300) + av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n"); + extend_af(buf, write_pcr_bits(q, pcr)); + q = get_ts_payload_start(buf); + } + if (is_start) { + int pes_extension = 0; + /* write PES header */ + *q++ = 0x00; + *q++ = 0x00; + *q++ = 0x01; + private_code = 0; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (st->codec->codec_id == AV_CODEC_ID_DIRAC) { + *q++ = 0xfd; + } else + *q++ = 0xe0; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + (st->codec->codec_id == AV_CODEC_ID_MP2 || + st->codec->codec_id == AV_CODEC_ID_MP3 || + st->codec->codec_id == AV_CODEC_ID_AAC)) { + *q++ = 0xc0; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3 && + ts->m2ts_mode) { + *q++ = 0xfd; + } else { + *q++ = 0xbd; + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + private_code = 0x20; + } + } + header_len = 0; + flags = 0; + if (pts != AV_NOPTS_VALUE) { + header_len += 5; + flags |= 0x80; + } + if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) { + header_len += 5; + flags |= 0x40; + } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + st->codec->codec_id == AV_CODEC_ID_DIRAC) { + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + + /* + * One byte for PES2 extension flag + + * one byte for extension length + + * one byte for extension id + */ + header_len += 3; + } + /* for Blu-ray AC3 Audio the PES Extension flag should be as follow + * otherwise it will not play sound on blu-ray + */ + if (ts->m2ts_mode && + st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->codec_id == AV_CODEC_ID_AC3) { + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + header_len += 3; + } + len = payload_size + header_len + 3; + if (private_code != 0) + len++; + if (len > 0xffff) + len = 0; + *q++ = len >> 8; + *q++ = len; + val = 0x80; + /* data alignment indicator is required for subtitle data */ + if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) + val |= 0x04; + *q++ = val; + *q++ = flags; + *q++ = header_len; + if (pts != AV_NOPTS_VALUE) { + write_pts(q, flags >> 6, pts); + q += 5; + } + if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) { + write_pts(q, 1, dts); + q += 5; + } + if (pes_extension && st->codec->codec_id == AV_CODEC_ID_DIRAC) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + /* + * Set the stream id extension flag bit to 0 and + * write the extended stream id + */ + *q++ = 0x00 | 0x60; + } + /* For Blu-ray AC3 Audio Setting extended flags */ + if (ts->m2ts_mode && + pes_extension && + st->codec->codec_id == AV_CODEC_ID_AC3) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ + } + + + if (private_code != 0) + *q++ = private_code; + is_start = 0; + } + /* header size */ + header_len = q - buf; + /* data len */ + len = TS_PACKET_SIZE - header_len; + if (len > payload_size) + len = payload_size; + stuffing_len = TS_PACKET_SIZE - header_len - len; + if (stuffing_len > 0) { + /* add stuffing with AFC */ + if (buf[3] & 0x20) { + /* stuffing already present: increase its size */ + afc_len = buf[4] + 1; + memmove(buf + 4 + afc_len + stuffing_len, + buf + 4 + afc_len, + header_len - (4 + afc_len)); + buf[4] += stuffing_len; + memset(buf + 4 + afc_len, 0xff, stuffing_len); + } else { + /* add stuffing */ + memmove(buf + 4 + stuffing_len, buf + 4, header_len - 4); + buf[3] |= 0x20; + buf[4] = stuffing_len - 1; + if (stuffing_len >= 2) { + buf[5] = 0x00; + memset(buf + 6, 0xff, stuffing_len - 2); + } + } + } + memcpy(buf + TS_PACKET_SIZE - len, payload, len); + payload += len; + payload_size -= len; + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); + } + avio_flush(s->pb); + ts_st->prev_payload_key = key; +} + +static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + int size = pkt->size; + uint8_t *buf= pkt->data; + uint8_t *data= NULL; + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st = st->priv_data; + const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE)*2; + int64_t dts = pkt->dts, pts = pkt->pts; + + if (ts->reemit_pat_pmt) { + av_log(s, AV_LOG_WARNING, "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); + ts->reemit_pat_pmt = 0; + ts->flags |= MPEGTS_FLAG_REEMIT_PAT_PMT; + } + + if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { + ts->pat_packet_count = ts->pat_packet_period - 1; + ts->sdt_packet_count = ts->sdt_packet_period - 1; + ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; + } + + if(ts->copyts < 1){ + if (pts != AV_NOPTS_VALUE) + pts += delay; + if (dts != AV_NOPTS_VALUE) + dts += delay; + } + + if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { + av_log(s, AV_LOG_ERROR, "first pts value must be set\n"); + return AVERROR_INVALIDDATA; + } + ts_st->first_pts_check = 0; + + if (st->codec->codec_id == AV_CODEC_ID_H264) { + const uint8_t *p = buf, *buf_end = p+size; + uint32_t state = -1; + + if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) { + if (!st->nb_frames) { + av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, " + "no startcode found, use the h264_mp4toannexb bitstream filter (-bsf h264_mp4toannexb)\n"); + return AVERROR(EINVAL); + } + av_log(s, AV_LOG_WARNING, "H.264 bitstream error, startcode missing\n"); + } + + do { + p = avpriv_find_start_code(p, buf_end, &state); + av_dlog(s, "nal %d\n", state & 0x1f); + } while (p < buf_end && (state & 0x1f) != 9 && + (state & 0x1f) != 5 && (state & 0x1f) != 1); + + if ((state & 0x1f) != 9) { // AUD NAL + data = av_malloc(pkt->size+6); + if (!data) + return AVERROR(ENOMEM); + memcpy(data+6, pkt->data, pkt->size); + AV_WB32(data, 0x00000001); + data[4] = 0x09; + data[5] = 0xf0; // any slice type (0xe) + rbsp stop one bit + buf = data; + size = pkt->size+6; + } + } else if (st->codec->codec_id == AV_CODEC_ID_AAC) { + if (pkt->size < 2) { + av_log(s, AV_LOG_ERROR, "AAC packet too short\n"); + return AVERROR_INVALIDDATA; + } + if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) { + int ret; + AVPacket pkt2; + + if (!ts_st->amux) { + av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format " + "and extradata missing\n"); + return AVERROR_INVALIDDATA; + } + + av_init_packet(&pkt2); + pkt2.data = pkt->data; + pkt2.size = pkt->size; + ret = avio_open_dyn_buf(&ts_st->amux->pb); + if (ret < 0) + return AVERROR(ENOMEM); + + ret = av_write_frame(ts_st->amux, &pkt2); + if (ret < 0) { + avio_close_dyn_buf(ts_st->amux->pb, &data); + ts_st->amux->pb = NULL; + av_free(data); + return ret; + } + size = avio_close_dyn_buf(ts_st->amux->pb, &data); + ts_st->amux->pb = NULL; + buf = data; + } + } + + if (pkt->dts != AV_NOPTS_VALUE) { + int i; + for(i=0; inb_streams; i++){ + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if( ts_st2->payload_size + && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)){ + mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, + ts_st2->payload_pts, ts_st2->payload_dts, + ts_st2->payload_flags & AV_PKT_FLAG_KEY); + ts_st2->payload_size = 0; + } + } + } + + if (ts_st->payload_size && ts_st->payload_size + size > ts->pes_payload_size) { + mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + } + + if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO || size > ts->pes_payload_size) { + av_assert0(!ts_st->payload_size); + // for video and subtitle, write a single pes packet + mpegts_write_pes(s, st, buf, size, pts, dts, pkt->flags & AV_PKT_FLAG_KEY); + av_free(data); + return 0; + } + + if (!ts_st->payload_size) { + ts_st->payload_pts = pts; + ts_st->payload_dts = dts; + ts_st->payload_flags = pkt->flags; + } + + memcpy(ts_st->payload + ts_st->payload_size, buf, size); + ts_st->payload_size += size; + + av_free(data); + + return 0; +} + +static void mpegts_write_flush(AVFormatContext *s) +{ + int i; + + /* flush current packets */ + for(i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MpegTSWriteStream *ts_st = st->priv_data; + if (ts_st->payload_size > 0) { + mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + } + } + avio_flush(s->pb); +} + +static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + if (!pkt) { + mpegts_write_flush(s); + return 1; + } else { + return mpegts_write_packet_internal(s, pkt); + } +} + +static int mpegts_write_end(AVFormatContext *s) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSService *service; + int i; + + mpegts_write_flush(s); + + for(i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MpegTSWriteStream *ts_st = st->priv_data; + av_freep(&ts_st->payload); + if (ts_st->amux) { + avformat_free_context(ts_st->amux); + ts_st->amux = NULL; + } + } + + for(i = 0; i < ts->nb_services; i++) { + service = ts->services[i]; + av_freep(&service->provider_name); + av_freep(&service->name); + av_free(service); + } + av_free(ts->services); + + return 0; +} + +AVOutputFormat ff_mpegts_muxer = { + .name = "mpegts", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), + .mime_type = "video/x-mpegts", + .extensions = "ts,m2t,m2ts,mts", + .priv_data_size = sizeof(MpegTSWrite), + .audio_codec = AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mpegts_write_header, + .write_packet = mpegts_write_packet, + .write_trailer = mpegts_write_end, + .flags = AVFMT_ALLOW_FLUSH, + .priv_class = &mpegts_muxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegtsenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegtsenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mpegtsenc.o libavformat/mpegtsenc.o: libavformat/mpegtsenc.c \ + libavutil/bswap.h libavutil/avconfig.h libavutil/attributes.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h libavutil/crc.h \ + libavutil/dict.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h libavutil/opt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/mathematics.h libavutil/intfloat_readwrite.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/version.h \ + libavutil/old_pix_fmts.h libavutil/samplefmt.h libavutil/avassert.h \ + libavcodec/internal.h libavutil/buffer.h libavutil/mathematics.h \ + libavutil/pixfmt.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/log.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + config.h libavformat/avformat.h libavcodec/avcodec.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/mpegts.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegtsenc.o Binary file ffmpeg/libavformat/mpegtsenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegvideodec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegvideodec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,72 @@ +/* + * RAW MPEG video demuxer + * Copyright (c) 2002-2003 Fabrice Bellard + * Copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawdec.h" + +#define SEQ_START_CODE 0x000001b3 +#define GOP_START_CODE 0x000001b8 +#define PICTURE_START_CODE 0x00000100 +#define SLICE_START_CODE 0x00000101 +#define PACK_START_CODE 0x000001ba +#define VIDEO_ID 0x000001e0 +#define AUDIO_ID 0x000001c0 + +static int mpegvideo_probe(AVProbeData *p) +{ + uint32_t code= -1; + int pic=0, seq=0, slice=0, pspack=0, vpes=0, apes=0, res=0, sicle=0; + int i; + uint32_t last = 0; + + for(i=0; ibuf_size; i++){ + code = (code<<8) + p->buf[i]; + if ((code & 0xffffff00) == 0x100) { + switch(code){ + case SEQ_START_CODE: seq++; break; + case PICTURE_START_CODE: pic++; break; + case PACK_START_CODE: pspack++; break; + case 0x1b6: + res++; break; + } + if (code >= SLICE_START_CODE && code <= 0x1af) { + if (last >= SLICE_START_CODE && last <= 0x1af) { + if (code >= last) slice++; + else sicle++; + }else{ + if (code == SLICE_START_CODE) slice++; + else sicle++; + } + } + if ((code & 0x1f0) == VIDEO_ID) vpes++; + else if((code & 0x1e0) == AUDIO_ID) apes++; + last = code; + } + } + if(seq && seq*9<=pic*10 && pic*9<=slice*10 && !pspack && !apes && !res && slice > sicle) { + if(vpes) return AVPROBE_SCORE_MAX/8; + else return pic>1 ? AVPROBE_SCORE_MAX/2+1 : AVPROBE_SCORE_MAX/4; // +1 for .mpg + } + return 0; +} + +FF_DEF_RAWVIDEO_DEMUXER(mpegvideo, "raw MPEG video", mpegvideo_probe, NULL, AV_CODEC_ID_MPEG1VIDEO) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegvideodec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpegvideodec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/mpegvideodec.o libavformat/mpegvideodec.o: \ + libavformat/mpegvideodec.c libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawdec.h \ + libavutil/opt.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpegvideodec.o Binary file ffmpeg/libavformat/mpegvideodec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpjpeg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpjpeg.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,68 @@ +/* + * Multipart JPEG format + * Copyright (c) 2000, 2001, 2002, 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" + +/* Multipart JPEG */ + +#define BOUNDARY_TAG "ffserver" + +static int mpjpeg_write_header(AVFormatContext *s) +{ + uint8_t buf1[256]; + + snprintf(buf1, sizeof(buf1), "--%s\r\n", BOUNDARY_TAG); + avio_write(s->pb, buf1, strlen(buf1)); + avio_flush(s->pb); + return 0; +} + +static int mpjpeg_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + uint8_t buf1[256]; + + snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\r\n"); + avio_write(s->pb, buf1, strlen(buf1)); + + snprintf(buf1, sizeof(buf1), "Content-length: %d\r\n\r\n", pkt->size); + avio_write(s->pb, buf1, strlen(buf1)); + avio_write(s->pb, pkt->data, pkt->size); + + snprintf(buf1, sizeof(buf1), "\r\n--%s\r\n", BOUNDARY_TAG); + avio_write(s->pb, buf1, strlen(buf1)); + return 0; +} + +static int mpjpeg_write_trailer(AVFormatContext *s) +{ + return 0; +} + +AVOutputFormat ff_mpjpeg_muxer = { + .name = "mpjpeg", + .long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"), + .mime_type = "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG, + .extensions = "mjpg", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_MJPEG, + .write_header = mpjpeg_write_header, + .write_packet = mpjpeg_write_packet, + .write_trailer = mpjpeg_write_trailer, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpjpeg.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpjpeg.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/mpjpeg.o libavformat/mpjpeg.o: libavformat/mpjpeg.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpjpeg.o Binary file ffmpeg/libavformat/mpjpeg.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpl2dec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpl2dec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MPL2 subtitles format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} MPL2Context; + +static int mpl2_probe(AVProbeData *p) +{ + int i; + char c; + int64_t start, end; + const unsigned char *ptr = p->buf; + const unsigned char *ptr_end = ptr + p->buf_size; + + for (i = 0; i < 2; i++) { + if (sscanf(ptr, "[%"SCNd64"][%"SCNd64"]%c", &start, &end, &c) != 3 && + sscanf(ptr, "[%"SCNd64"][]%c", &start, &c) != 2) + return 0; + ptr += strcspn(ptr, "\r\n") + 1; + if (ptr >= ptr_end) + return 0; + } + return AVPROBE_SCORE_MAX; +} + +static int read_ts(char **line, int64_t *pts_start, int *duration) +{ + char c; + int len; + int64_t end; + + if (sscanf(*line, "[%"SCNd64"][]%c%n", + pts_start, &c, &len) >= 2) { + *duration = -1; + *line += len - 1; + return 0; + } + if (sscanf(*line, "[%"SCNd64"][%"SCNd64"]%c%n", + pts_start, &end, &c, &len) >= 3) { + *duration = end - *pts_start; + *line += len - 1; + return 0; + } + return -1; +} + +static int mpl2_read_header(AVFormatContext *s) +{ + MPL2Context *mpl2 = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int res = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 10); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_MPL2; + + while (!url_feof(s->pb)) { + char line[4096]; + char *p = line; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + int64_t pts_start; + int duration; + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!read_ts(&p, &pts_start, &duration)) { + AVPacket *sub; + + sub = ff_subtitles_queue_insert(&mpl2->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + } + + ff_subtitles_queue_finalize(&mpl2->q); + return res; +} + +static int mpl2_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPL2Context *mpl2 = s->priv_data; + return ff_subtitles_queue_read_packet(&mpl2->q, pkt); +} + +static int mpl2_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MPL2Context *mpl2 = s->priv_data; + return ff_subtitles_queue_seek(&mpl2->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int mpl2_read_close(AVFormatContext *s) +{ + MPL2Context *mpl2 = s->priv_data; + ff_subtitles_queue_clean(&mpl2->q); + return 0; +} + +AVInputFormat ff_mpl2_demuxer = { + .name = "mpl2", + .long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"), + .priv_data_size = sizeof(MPL2Context), + .read_probe = mpl2_probe, + .read_header = mpl2_read_header, + .read_packet = mpl2_read_packet, + .read_seek2 = mpl2_read_seek, + .read_close = mpl2_read_close, + .extensions = "txt,mpl2", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpl2dec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpl2dec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/mpl2dec.o libavformat/mpl2dec.o: libavformat/mpl2dec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpl2dec.o Binary file ffmpeg/libavformat/mpl2dec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpsubdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpsubdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MPlayer subtitles format demuxer + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} MPSubContext; + +static int mpsub_probe(AVProbeData *p) +{ + const char *ptr = p->buf; + const char *ptr_end = p->buf + p->buf_size; + + while (ptr < ptr_end) { + int n; + + if (!memcmp(ptr, "FORMAT=TIME", 11) || + sscanf(ptr, "FORMAT=%d", &n) == 1) + return AVPROBE_SCORE_MAX/2; + ptr += strcspn(ptr, "\n") + 1; + } + return 0; +} + +static int mpsub_read_header(AVFormatContext *s) +{ + MPSubContext *mpsub = s->priv_data; + AVStream *st; + AVBPrint buf; + AVRational pts_info = (AVRational){ 100, 1 }; // ts based by default + int res = 0; + float multiplier = 100.0; + float current_pts = 0; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + char line[1024]; + float start, duration; + int fps, len = ff_get_line(s->pb, line, sizeof(line)); + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (sscanf(line, "FORMAT=%d", &fps) == 1 && fps > 3 && fps < 100) { + /* frame based timing */ + pts_info = (AVRational){ fps, 1 }; + multiplier = 1.0; + } else if (sscanf(line, "%f %f", &start, &duration) == 2) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb); + + ff_subtitles_read_chunk(s->pb, &buf); + if (buf.len) { + sub = ff_subtitles_queue_insert(&mpsub->q, buf.str, buf.len, 0); + if (!sub) { + res = AVERROR(ENOMEM); + goto end; + } + sub->pts = (int64_t)(current_pts + start*multiplier); + sub->duration = (int)(duration * multiplier); + current_pts += (start + duration) * multiplier; + sub->pos = pos; + } + } + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + + ff_subtitles_queue_finalize(&mpsub->q); + +end: + av_bprint_finalize(&buf, NULL); + return res; +} + +static int mpsub_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MPSubContext *mpsub = s->priv_data; + return ff_subtitles_queue_read_packet(&mpsub->q, pkt); +} + +static int mpsub_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + MPSubContext *mpsub = s->priv_data; + return ff_subtitles_queue_seek(&mpsub->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int mpsub_read_close(AVFormatContext *s) +{ + MPSubContext *mpsub = s->priv_data; + ff_subtitles_queue_clean(&mpsub->q); + return 0; +} + +AVInputFormat ff_mpsub_demuxer = { + .name = "mpsub", + .long_name = NULL_IF_CONFIG_SMALL("MPlayer subtitles"), + .priv_data_size = sizeof(MPSubContext), + .read_probe = mpsub_probe, + .read_header = mpsub_read_header, + .read_packet = mpsub_read_packet, + .read_seek2 = mpsub_read_seek, + .read_close = mpsub_read_close, + .extensions = "sub", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpsubdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mpsubdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/mpsubdec.o libavformat/mpsubdec.o: libavformat/mpsubdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mpsubdec.o Binary file ffmpeg/libavformat/mpsubdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/msnwc_tcp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/msnwc_tcp.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008 Ramiro Polla + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" + +#define HEADER_SIZE 24 + +/* + * Header structure: + * uint16_t ss; // struct size + * uint16_t width; // frame width + * uint16_t height; // frame height + * uint16_t ff; // keyframe + some other info(???) + * uint32_t size; // size of data + * uint32_t fourcc; // ML20 + * uint32_t u3; // ? + * uint32_t ts; // time + */ + +static int msnwc_tcp_probe(AVProbeData *p) +{ + int i; + + for(i = 0 ; i + HEADER_SIZE <= p->buf_size ; i++) { + uint16_t width, height; + uint32_t fourcc; + const uint8_t *bytestream = p->buf+i; + + if(bytestream_get_le16(&bytestream) != HEADER_SIZE) + continue; + width = bytestream_get_le16(&bytestream); + height = bytestream_get_le16(&bytestream); + if(!(width==320 && height==240) && !(width==160 && height==120)) + continue; + bytestream += 2; // keyframe + bytestream += 4; // size + fourcc = bytestream_get_le32(&bytestream); + if(fourcc != MKTAG('M', 'L', '2', '0')) + continue; + + if(i) { + if(i < 14) /* starts with SwitchBoard connection info */ + return AVPROBE_SCORE_MAX / 2; + else /* starts in the middle of stream */ + return AVPROBE_SCORE_MAX / 3; + } else { + return AVPROBE_SCORE_MAX; + } + } + + return -1; +} + +static int msnwc_tcp_read_header(AVFormatContext *ctx) +{ + AVIOContext *pb = ctx->pb; + AVCodecContext *codec; + AVStream *st; + + st = avformat_new_stream(ctx, NULL); + if(!st) + return AVERROR(ENOMEM); + + codec = st->codec; + codec->codec_type = AVMEDIA_TYPE_VIDEO; + codec->codec_id = AV_CODEC_ID_MIMIC; + codec->codec_tag = MKTAG('M', 'L', '2', '0'); + + avpriv_set_pts_info(st, 32, 1, 1000); + + /* Some files start with "connected\r\n\r\n". + * So skip until we find the first byte of struct size */ + while(avio_r8(pb) != HEADER_SIZE && !url_feof(pb)); + + if(url_feof(pb)) { + av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n"); + return -1; + } + + return 0; +} + +static int msnwc_tcp_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + AVIOContext *pb = ctx->pb; + uint16_t keyframe; + uint32_t size, timestamp; + + avio_skip(pb, 1); /* one byte has been read ahead */ + avio_skip(pb, 2); + avio_skip(pb, 2); + keyframe = avio_rl16(pb); + size = avio_rl32(pb); + avio_skip(pb, 4); + avio_skip(pb, 4); + timestamp = avio_rl32(pb); + + if(!size || av_get_packet(pb, pkt, size) != size) + return -1; + + avio_skip(pb, 1); /* Read ahead one byte of struct size like read_header */ + + pkt->pts = timestamp; + pkt->dts = timestamp; + pkt->stream_index = 0; + + /* Some aMsn generated videos (or was it Mercury Messenger?) don't set + * this bit and rely on the codec to get keyframe information */ + if(keyframe&1) + pkt->flags |= AV_PKT_FLAG_KEY; + + return HEADER_SIZE + size; +} + +AVInputFormat ff_msnwc_tcp_demuxer = { + .name = "msnwctcp", + .long_name = NULL_IF_CONFIG_SMALL("MSN TCP Webcam stream"), + .read_probe = msnwc_tcp_probe, + .read_header = msnwc_tcp_read_header, + .read_packet = msnwc_tcp_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/msnwc_tcp.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/msnwc_tcp.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/msnwc_tcp.o libavformat/msnwc_tcp.o: libavformat/msnwc_tcp.c \ + libavcodec/bytestream.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/msnwc_tcp.o Binary file ffmpeg/libavformat/msnwc_tcp.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mtv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mtv.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,203 @@ +/* + * mtv demuxer + * Copyright (c) 2006 Reynaldo H. Verdejo Pinochet + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MTV demuxer. + */ + +#include "libavutil/bswap.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define MTV_ASUBCHUNK_DATA_SIZE 500 +#define MTV_HEADER_SIZE 512 +#define MTV_AUDIO_PADDING_SIZE 12 +#define AUDIO_SAMPLING_RATE 44100 + +typedef struct MTVDemuxContext { + + unsigned int file_size; ///< filesize, not always right + unsigned int segments; ///< number of 512 byte segments + unsigned int audio_identifier; ///< 'MP3' on all files I have seen + unsigned int audio_br; ///< bitrate of audio channel (mp3) + unsigned int img_colorfmt; ///< frame colorfmt rgb 565/555 + unsigned int img_bpp; ///< frame bits per pixel + unsigned int img_width; // + unsigned int img_height; // + unsigned int img_segment_size; ///< size of image segment + unsigned int video_fps; // + unsigned int full_segment_size; + +} MTVDemuxContext; + +static int mtv_probe(AVProbeData *p) +{ + /* Magic is 'AMV' */ + if (*p->buf != 'A' || *(p->buf + 1) != 'M' || *(p->buf + 2) != 'V') + return 0; + + /* Check for nonzero in bpp and (width|height) header fields */ + if(p->buf_size < 57 || !(p->buf[51] && AV_RL16(&p->buf[52]) | AV_RL16(&p->buf[54]))) + return 0; + + /* If width or height are 0 then imagesize header field should not */ + if(!AV_RL16(&p->buf[52]) || !AV_RL16(&p->buf[54])) + { + if(!!AV_RL16(&p->buf[56])) + return AVPROBE_SCORE_MAX/2; + else + return 0; + } + + if(p->buf[51] != 16) + return AVPROBE_SCORE_MAX/4; // But we are going to assume 16bpp anyway .. + + return AVPROBE_SCORE_MAX; +} + +static int mtv_read_header(AVFormatContext *s) +{ + MTVDemuxContext *mtv = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + unsigned int audio_subsegments; + + avio_skip(pb, 3); + mtv->file_size = avio_rl32(pb); + mtv->segments = avio_rl32(pb); + avio_skip(pb, 32); + mtv->audio_identifier = avio_rl24(pb); + mtv->audio_br = avio_rl16(pb); + mtv->img_colorfmt = avio_rl24(pb); + mtv->img_bpp = avio_r8(pb); + mtv->img_width = avio_rl16(pb); + mtv->img_height = avio_rl16(pb); + mtv->img_segment_size = avio_rl16(pb); + + /* Calculate width and height if missing from header */ + + if(mtv->img_bpp>>3){ + if(!mtv->img_width && mtv->img_height) + mtv->img_width=mtv->img_segment_size / (mtv->img_bpp>>3) + / mtv->img_height; + + if(!mtv->img_height && mtv->img_width) + mtv->img_height=mtv->img_segment_size / (mtv->img_bpp>>3) + / mtv->img_width; + } + if(!mtv->img_height || !mtv->img_width){ + av_log(s, AV_LOG_ERROR, "width or height is invalid and I cannot calculate them from other information\n"); + return AVERROR(EINVAL); + } + + avio_skip(pb, 4); + audio_subsegments = avio_rl16(pb); + + if (audio_subsegments == 0) { + avpriv_request_sample(s, "MTV files without audio"); + return AVERROR_PATCHWELCOME; + } + + mtv->full_segment_size = + audio_subsegments * (MTV_AUDIO_PADDING_SIZE + MTV_ASUBCHUNK_DATA_SIZE) + + mtv->img_segment_size; + mtv->video_fps = (mtv->audio_br / 4) / audio_subsegments; + + // FIXME Add sanity check here + + // all systems go! init decoders + + // video - raw rgb565 + + st = avformat_new_stream(s, NULL); + if(!st) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(st, 64, 1, mtv->video_fps); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->pix_fmt = AV_PIX_FMT_RGB565BE; + st->codec->width = mtv->img_width; + st->codec->height = mtv->img_height; + st->codec->sample_rate = mtv->video_fps; + st->codec->extradata = av_strdup("BottomUp"); + st->codec->extradata_size = 9; + + // audio - mp3 + + st = avformat_new_stream(s, NULL); + if(!st) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(st, 64, 1, AUDIO_SAMPLING_RATE); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MP3; + st->codec->bit_rate = mtv->audio_br; + st->need_parsing = AVSTREAM_PARSE_FULL; + + // Jump over header + + if(avio_seek(pb, MTV_HEADER_SIZE, SEEK_SET) != MTV_HEADER_SIZE) + return AVERROR(EIO); + + return 0; + +} + +static int mtv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MTVDemuxContext *mtv = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + + if((avio_tell(pb) - s->data_offset + mtv->img_segment_size) % mtv->full_segment_size) + { + avio_skip(pb, MTV_AUDIO_PADDING_SIZE); + + ret = av_get_packet(pb, pkt, MTV_ASUBCHUNK_DATA_SIZE); + if(ret < 0) + return ret; + + pkt->pos -= MTV_AUDIO_PADDING_SIZE; + pkt->stream_index = 1; + + }else + { + ret = av_get_packet(pb, pkt, mtv->img_segment_size); + if(ret < 0) + return ret; + + pkt->stream_index = 0; + } + + return ret; +} + +AVInputFormat ff_mtv_demuxer = { + .name = "mtv", + .long_name = NULL_IF_CONFIG_SMALL("MTV"), + .priv_data_size = sizeof(MTVDemuxContext), + .read_probe = mtv_probe, + .read_header = mtv_read_header, + .read_packet = mtv_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mtv.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mtv.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/mtv.o libavformat/mtv.o: libavformat/mtv.c libavutil/bswap.h \ + libavutil/avconfig.h libavutil/attributes.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mtv.o Binary file ffmpeg/libavformat/mtv.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mux.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,818 @@ +/* + * muxing functions for use within FFmpeg + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* #define DEBUG */ + +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "libavcodec/internal.h" +#include "libavcodec/bytestream.h" +#include "libavutil/opt.h" +#include "libavutil/dict.h" +#include "libavutil/pixdesc.h" +#include "libavutil/timestamp.h" +#include "metadata.h" +#include "id3v2.h" +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/mathematics.h" +#include "libavutil/parseutils.h" +#include "libavutil/time.h" +#include "riff.h" +#include "audiointerleave.h" +#include "url.h" +#include +#if CONFIG_NETWORK +#include "network.h" +#endif + +#undef NDEBUG +#include + +/** + * @file + * muxing functions for use within libavformat + */ + +/* fraction handling */ + +/** + * f = val + (num / den) + 0.5. + * + * 'num' is normalized so that it is such as 0 <= num < den. + * + * @param f fractional number + * @param val integer value + * @param num must be >= 0 + * @param den must be >= 1 + */ +static void frac_init(AVFrac *f, int64_t val, int64_t num, int64_t den) +{ + num += (den >> 1); + if (num >= den) { + val += num / den; + num = num % den; + } + f->val = val; + f->num = num; + f->den = den; +} + +/** + * Fractional addition to f: f = f + (incr / f->den). + * + * @param f fractional number + * @param incr increment, can be positive or negative + */ +static void frac_add(AVFrac *f, int64_t incr) +{ + int64_t num, den; + + num = f->num + incr; + den = f->den; + if (num < 0) { + f->val += num / den; + num = num % den; + if (num < 0) { + num += den; + f->val--; + } + } else if (num >= den) { + f->val += num / den; + num = num % den; + } + f->num = num; +} + +AVRational ff_choose_timebase(AVFormatContext *s, AVStream *st, int min_precission) +{ + AVRational q; + int j; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + q = (AVRational){1, st->codec->sample_rate}; + } else { + q = st->codec->time_base; + } + for (j=2; j<14; j+= 1+(j>2)) + while (q.den / q.num < min_precission && q.num % j == 0) + q.num /= j; + while (q.den / q.num < min_precission && q.den < (1<<24)) + q.den <<= 1; + + return q; +} + +int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, + const char *format, const char *filename) +{ + AVFormatContext *s = avformat_alloc_context(); + int ret = 0; + + *avctx = NULL; + if (!s) + goto nomem; + + if (!oformat) { + if (format) { + oformat = av_guess_format(format, NULL, NULL); + if (!oformat) { + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + ret = AVERROR(EINVAL); + goto error; + } + } else { + oformat = av_guess_format(NULL, filename, NULL); + if (!oformat) { + ret = AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", + filename); + goto error; + } + } + } + + s->oformat = oformat; + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (!s->priv_data) + goto nomem; + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data= s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } + } else + s->priv_data = NULL; + + if (filename) + av_strlcpy(s->filename, filename, sizeof(s->filename)); + *avctx = s; + return 0; +nomem: + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); +error: + avformat_free_context(s); + return ret; +} + +#if FF_API_ALLOC_OUTPUT_CONTEXT +AVFormatContext *avformat_alloc_output_context(const char *format, + AVOutputFormat *oformat, const char *filename) +{ + AVFormatContext *avctx; + int ret = avformat_alloc_output_context2(&avctx, oformat, format, filename); + return ret < 0 ? NULL : avctx; +} +#endif + +static int validate_codec_tag(AVFormatContext *s, AVStream *st) +{ + const AVCodecTag *avctag; + int n; + enum AVCodecID id = AV_CODEC_ID_NONE; + unsigned int tag = 0; + + /** + * Check that tag + id is in the table + * If neither is in the table -> OK + * If tag is in the table with another id -> FAIL + * If id is in the table with another tag -> FAIL unless strict < normal + */ + for (n = 0; s->oformat->codec_tag[n]; n++) { + avctag = s->oformat->codec_tag[n]; + while (avctag->id != AV_CODEC_ID_NONE) { + if (avpriv_toupper4(avctag->tag) == avpriv_toupper4(st->codec->codec_tag)) { + id = avctag->id; + if (id == st->codec->codec_id) + return 1; + } + if (avctag->id == st->codec->codec_id) + tag = avctag->tag; + avctag++; + } + } + if (id != AV_CODEC_ID_NONE) + return 0; + if (tag && (st->codec->strict_std_compliance >= FF_COMPLIANCE_NORMAL)) + return 0; + return 1; +} + + +static int init_muxer(AVFormatContext *s, AVDictionary **options) +{ + int ret = 0, i; + AVStream *st; + AVDictionary *tmp = NULL; + AVCodecContext *codec = NULL; + AVOutputFormat *of = s->oformat; + + if (options) + av_dict_copy(&tmp, *options, 0); + + if ((ret = av_opt_set_dict(s, &tmp)) < 0) + goto fail; + if (s->priv_data && s->oformat->priv_class && *(const AVClass**)s->priv_data==s->oformat->priv_class && + (ret = av_opt_set_dict(s->priv_data, &tmp)) < 0) + goto fail; + + // some sanity checks + if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) { + av_log(s, AV_LOG_ERROR, "no streams\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + for (i = 0; i < s->nb_streams; i++) { + st = s->streams[i]; + codec = st->codec; + + switch (codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if (codec->sample_rate <= 0) { + av_log(s, AV_LOG_ERROR, "sample rate not set\n"); + ret = AVERROR(EINVAL); + goto fail; + } + if (!codec->block_align) + codec->block_align = codec->channels * + av_get_bits_per_sample(codec->codec_id) >> 3; + break; + case AVMEDIA_TYPE_VIDEO: + if (codec->time_base.num <= 0 || + codec->time_base.den <= 0) { //FIXME audio too? + av_log(s, AV_LOG_ERROR, "time base not set\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if ((codec->width <= 0 || codec->height <= 0) && + !(of->flags & AVFMT_NODIMENSIONS)) { + av_log(s, AV_LOG_ERROR, "dimensions not set\n"); + ret = AVERROR(EINVAL); + goto fail; + } + if (av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio) + && FFABS(av_q2d(st->sample_aspect_ratio) - av_q2d(codec->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio) + ) { + av_log(s, AV_LOG_ERROR, "Aspect ratio mismatch between muxer " + "(%d/%d) and encoder layer (%d/%d)\n", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, + codec->sample_aspect_ratio.num, + codec->sample_aspect_ratio.den); + ret = AVERROR(EINVAL); + goto fail; + } + break; + } + + if (of->codec_tag) { + if ( codec->codec_tag + && codec->codec_id == AV_CODEC_ID_RAWVIDEO + && ( av_codec_get_tag(of->codec_tag, codec->codec_id) == 0 + || av_codec_get_tag(of->codec_tag, codec->codec_id) == MKTAG('r', 'a', 'w', ' ')) + && !validate_codec_tag(s, st)) { + // the current rawvideo encoding system ends up setting + // the wrong codec_tag for avi/mov, we override it here + codec->codec_tag = 0; + } + if (codec->codec_tag) { + if (!validate_codec_tag(s, st)) { + char tagbuf[32], cortag[32]; + av_get_codec_tag_string(tagbuf, sizeof(tagbuf), codec->codec_tag); + av_get_codec_tag_string(cortag, sizeof(cortag), av_codec_get_tag(s->oformat->codec_tag, codec->codec_id)); + av_log(s, AV_LOG_ERROR, + "Tag %s/0x%08x incompatible with output codec id '%d' (%s)\n", + tagbuf, codec->codec_tag, codec->codec_id, cortag); + ret = AVERROR_INVALIDDATA; + goto fail; + } + } else + codec->codec_tag = av_codec_get_tag(of->codec_tag, codec->codec_id); + } + + if (of->flags & AVFMT_GLOBALHEADER && + !(codec->flags & CODEC_FLAG_GLOBAL_HEADER)) + av_log(s, AV_LOG_WARNING, + "Codec for stream %d does not use global headers " + "but container format requires global headers\n", i); + } + + if (!s->priv_data && of->priv_data_size > 0) { + s->priv_data = av_mallocz(of->priv_data_size); + if (!s->priv_data) { + ret = AVERROR(ENOMEM); + goto fail; + } + if (of->priv_class) { + *(const AVClass **)s->priv_data = of->priv_class; + av_opt_set_defaults(s->priv_data); + if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0) + goto fail; + } + } + + /* set muxer identification string */ + if (s->nb_streams && !(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) { + av_dict_set(&s->metadata, "encoder", LIBAVFORMAT_IDENT, 0); + } + + if (options) { + av_dict_free(options); + *options = tmp; + } + + return 0; + +fail: + av_dict_free(&tmp); + return ret; +} + +static int init_pts(AVFormatContext *s) +{ + int i; + AVStream *st; + + /* init PTS generation */ + for (i = 0; i < s->nb_streams; i++) { + int64_t den = AV_NOPTS_VALUE; + st = s->streams[i]; + + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + den = (int64_t)st->time_base.num * st->codec->sample_rate; + break; + case AVMEDIA_TYPE_VIDEO: + den = (int64_t)st->time_base.num * st->codec->time_base.den; + break; + default: + break; + } + if (den != AV_NOPTS_VALUE) { + if (den <= 0) + return AVERROR_INVALIDDATA; + + frac_init(&st->pts, 0, 0, den); + } + } + + return 0; +} + +int avformat_write_header(AVFormatContext *s, AVDictionary **options) +{ + int ret = 0; + + if (ret = init_muxer(s, options)) + return ret; + + if (s->oformat->write_header) { + ret = s->oformat->write_header(s); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + if (ret < 0) + return ret; + } + + if ((ret = init_pts(s)) < 0) + return ret; + + return 0; +} + +//FIXME merge with compute_pkt_fields +static int compute_pkt_fields2(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + int delay = FFMAX(st->codec->has_b_frames, st->codec->max_b_frames > 0); + int num, den, frame_size, i; + + av_dlog(s, "compute_pkt_fields2: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index); + + /* duration field */ + if (pkt->duration == 0) { + ff_compute_frame_duration(&num, &den, st, NULL, pkt); + if (den && num) { + pkt->duration = av_rescale(1, num * (int64_t)st->time_base.den * st->codec->ticks_per_frame, den * (int64_t)st->time_base.num); + } + } + + if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && delay == 0) + pkt->pts = pkt->dts; + + //XXX/FIXME this is a temporary hack until all encoders output pts + if ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) { + static int warned; + if (!warned) { + av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n"); + warned = 1; + } + pkt->dts = +// pkt->pts= st->cur_dts; + pkt->pts = st->pts.val; + } + + //calculate dts from pts + if (pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) { + st->pts_buffer[0] = pkt->pts; + for (i = 1; i < delay + 1 && st->pts_buffer[i] == AV_NOPTS_VALUE; i++) + st->pts_buffer[i] = pkt->pts + (i - delay - 1) * pkt->duration; + for (i = 0; ipts_buffer[i] > st->pts_buffer[i + 1]; i++) + FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]); + + pkt->dts = st->pts_buffer[0]; + } + + if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE && + ((!(s->oformat->flags & AVFMT_TS_NONSTRICT) && + st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) { + av_log(s, AV_LOG_ERROR, + "Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n", + st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts)); + return AVERROR(EINVAL); + } + if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts) { + av_log(s, AV_LOG_ERROR, "pts (%s) < dts (%s) in stream %d\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts), st->index); + return AVERROR(EINVAL); + } + + av_dlog(s, "av_write_frame: pts2:%s dts2:%s\n", + av_ts2str(pkt->pts), av_ts2str(pkt->dts)); + st->cur_dts = pkt->dts; + st->pts.val = pkt->dts; + + /* update pts */ + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + frame_size = ff_get_audio_frame_size(st->codec, pkt->size, 1); + + /* HACK/FIXME, we skip the initial 0 size packets as they are most + * likely equal to the encoder delay, but it would be better if we + * had the real timestamps from the encoder */ + if (frame_size >= 0 && (pkt->size || st->pts.num != st->pts.den >> 1 || st->pts.val)) { + frac_add(&st->pts, (int64_t)st->time_base.den * frame_size); + } + break; + case AVMEDIA_TYPE_VIDEO: + frac_add(&st->pts, (int64_t)st->time_base.den * st->codec->time_base.num); + break; + default: + break; + } + return 0; +} + +/** + * Move side data from payload to internal struct, call muxer, and restore + * original packet. + */ +static inline int split_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, did_split; + + did_split = av_packet_split_side_data(pkt); + ret = s->oformat->write_packet(s, pkt); + if (s->flush_packets && s->pb && s->pb->error >= 0) + avio_flush(s->pb); + if (did_split) + av_packet_merge_side_data(pkt); + return ret; +} + +int av_write_frame(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + + if (!pkt) { + if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { + ret = s->oformat->write_packet(s, NULL); + if (s->flush_packets && s->pb && s->pb->error >= 0) + avio_flush(s->pb); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + return ret; + } + return 1; + } + + ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt); + + if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) + return ret; + + ret = split_write_packet(s, pkt); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + + if (ret >= 0) + s->streams[pkt->stream_index]->nb_frames++; + return ret; +} + +#define CHUNK_START 0x1000 + +int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, + int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)) +{ + AVPacketList **next_point, *this_pktl; + AVStream *st = s->streams[pkt->stream_index]; + int chunked = s->max_chunk_size || s->max_chunk_duration; + + this_pktl = av_mallocz(sizeof(AVPacketList)); + if (!this_pktl) + return AVERROR(ENOMEM); + this_pktl->pkt = *pkt; +#if FF_API_DESTRUCT_PACKET + pkt->destruct = NULL; // do not free original but only the copy +#endif + pkt->buf = NULL; + av_dup_packet(&this_pktl->pkt); // duplicate the packet if it uses non-allocated memory + + if (s->streams[pkt->stream_index]->last_in_packet_buffer) { + next_point = &(st->last_in_packet_buffer->next); + } else { + next_point = &s->packet_buffer; + } + + if (chunked) { + uint64_t max= av_rescale_q_rnd(s->max_chunk_duration, AV_TIME_BASE_Q, st->time_base, AV_ROUND_UP); + st->interleaver_chunk_size += pkt->size; + st->interleaver_chunk_duration += pkt->duration; + if ( (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size) + || (max && st->interleaver_chunk_duration > max)) { + st->interleaver_chunk_size = 0; + this_pktl->pkt.flags |= CHUNK_START; + if (max && st->interleaver_chunk_duration > max) { + int64_t syncoffset = (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)*max/2; + int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset; + + st->interleaver_chunk_duration += (pkt->dts - syncto)/8 - max; + } else + st->interleaver_chunk_duration = 0; + } + } + if (*next_point) { + if (chunked && !(this_pktl->pkt.flags & CHUNK_START)) + goto next_non_null; + + if (compare(s, &s->packet_buffer_end->pkt, pkt)) { + while ( *next_point + && ((chunked && !((*next_point)->pkt.flags&CHUNK_START)) + || !compare(s, &(*next_point)->pkt, pkt))) + next_point = &(*next_point)->next; + if (*next_point) + goto next_non_null; + } else { + next_point = &(s->packet_buffer_end->next); + } + } + av_assert1(!*next_point); + + s->packet_buffer_end = this_pktl; +next_non_null: + + this_pktl->next = *next_point; + + s->streams[pkt->stream_index]->last_in_packet_buffer = + *next_point = this_pktl; + return 0; +} + +static int ff_interleave_compare_dts(AVFormatContext *s, AVPacket *next, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + AVStream *st2 = s->streams[next->stream_index]; + int comp = av_compare_ts(next->dts, st2->time_base, pkt->dts, + st->time_base); + if (s->audio_preload && ((st->codec->codec_type == AVMEDIA_TYPE_AUDIO) != (st2->codec->codec_type == AVMEDIA_TYPE_AUDIO))) { + int64_t ts = av_rescale_q(pkt ->dts, st ->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO); + int64_t ts2= av_rescale_q(next->dts, st2->time_base, AV_TIME_BASE_Q) - s->audio_preload*(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO); + if (ts == ts2) { + ts= ( pkt ->dts* st->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st ->codec->codec_type == AVMEDIA_TYPE_AUDIO)* st->time_base.den)*st2->time_base.den + -( next->dts*st2->time_base.num*AV_TIME_BASE - s->audio_preload*(int64_t)(st2->codec->codec_type == AVMEDIA_TYPE_AUDIO)*st2->time_base.den)* st->time_base.den; + ts2=0; + } + comp= (ts>ts2) - (tsstream_index < next->stream_index; + return comp > 0; +} + +int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, + AVPacket *pkt, int flush) +{ + AVPacketList *pktl; + int stream_count = 0, noninterleaved_count = 0; + int64_t delta_dts_max = 0; + int i, ret; + + if (pkt) { + ret = ff_interleave_add_packet(s, pkt, ff_interleave_compare_dts); + if (ret < 0) + return ret; + } + + for (i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + ++stream_count; + } else if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { + ++noninterleaved_count; + } + } + + if (s->nb_streams == stream_count) { + flush = 1; + } else if (!flush) { + for (i=0; i < s->nb_streams; i++) { + if (s->streams[i]->last_in_packet_buffer) { + int64_t delta_dts = + av_rescale_q(s->streams[i]->last_in_packet_buffer->pkt.dts, + s->streams[i]->time_base, + AV_TIME_BASE_Q) - + av_rescale_q(s->packet_buffer->pkt.dts, + s->streams[s->packet_buffer->pkt.stream_index]->time_base, + AV_TIME_BASE_Q); + delta_dts_max= FFMAX(delta_dts_max, delta_dts); + } + } + if (s->nb_streams == stream_count+noninterleaved_count && + delta_dts_max > 20*AV_TIME_BASE) { + av_log(s, AV_LOG_DEBUG, "flushing with %d noninterleaved\n", noninterleaved_count); + flush = 1; + } + } + if (stream_count && flush) { + AVStream *st; + pktl = s->packet_buffer; + *out = pktl->pkt; + st = s->streams[out->stream_index]; + + s->packet_buffer = pktl->next; + if (!s->packet_buffer) + s->packet_buffer_end = NULL; + + if (st->last_in_packet_buffer == pktl) + st->last_in_packet_buffer = NULL; + av_freep(&pktl); + + if (s->avoid_negative_ts > 0) { + if (out->dts != AV_NOPTS_VALUE) { + if (!st->mux_ts_offset && out->dts < 0) { + for (i = 0; i < s->nb_streams; i++) { + s->streams[i]->mux_ts_offset = + av_rescale_q_rnd(-out->dts, + st->time_base, + s->streams[i]->time_base, + AV_ROUND_UP); + } + } + out->dts += st->mux_ts_offset; + } + if (out->pts != AV_NOPTS_VALUE) + out->pts += st->mux_ts_offset; + } + + return 1; + } else { + av_init_packet(out); + return 0; + } +} + +/** + * Interleave an AVPacket correctly so it can be muxed. + * @param out the interleaved packet will be output here + * @param in the input packet + * @param flush 1 if no further packets are available as input and all + * remaining packets should be output + * @return 1 if a packet was output, 0 if no packet could be output, + * < 0 if an error occurred + */ +static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush) +{ + if (s->oformat->interleave_packet) { + int ret = s->oformat->interleave_packet(s, out, in, flush); + if (in) + av_free_packet(in); + return ret; + } else + return ff_interleave_packet_per_dts(s, out, in, flush); +} + +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) +{ + int ret, flush = 0; + + if (pkt) { + AVStream *st = s->streams[pkt->stream_index]; + + //FIXME/XXX/HACK drop zero sized packets + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && pkt->size == 0) + return 0; + + av_dlog(s, "av_interleaved_write_frame size:%d dts:%s pts:%s\n", + pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); + if ((ret = compute_pkt_fields2(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) + return ret; + + if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) + return AVERROR(EINVAL); + } else { + av_dlog(s, "av_interleaved_write_frame FLUSH\n"); + flush = 1; + } + + for (;; ) { + AVPacket opkt; + int ret = interleave_packet(s, &opkt, pkt, flush); + if (ret <= 0) //FIXME cleanup needed for ret<0 ? + return ret; + + ret = split_write_packet(s, &opkt); + if (ret >= 0) + s->streams[opkt.stream_index]->nb_frames++; + + av_free_packet(&opkt); + pkt = NULL; + + if (ret < 0) + return ret; + if(s->pb && s->pb->error) + return s->pb->error; + } +} + +int av_write_trailer(AVFormatContext *s) +{ + int ret, i; + + for (;; ) { + AVPacket pkt; + ret = interleave_packet(s, &pkt, NULL, 1); + if (ret < 0) //FIXME cleanup needed for ret<0 ? + goto fail; + if (!ret) + break; + + ret = split_write_packet(s, &pkt); + if (ret >= 0) + s->streams[pkt.stream_index]->nb_frames++; + + av_free_packet(&pkt); + + if (ret < 0) + goto fail; + if(s->pb && s->pb->error) + goto fail; + } + + if (s->oformat->write_trailer) + ret = s->oformat->write_trailer(s); + +fail: + if (s->pb) + avio_flush(s->pb); + if (ret == 0) + ret = s->pb ? s->pb->error : 0; + for (i = 0; i < s->nb_streams; i++) { + av_freep(&s->streams[i]->priv_data); + av_freep(&s->streams[i]->index_entries); + } + if (s->oformat->priv_class) + av_opt_free(s->priv_data); + av_freep(&s->priv_data); + return ret; +} + +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall) +{ + if (!s->oformat || !s->oformat->get_output_timestamp) + return AVERROR(ENOSYS); + s->oformat->get_output_timestamp(s, stream, dts, wall); + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mux.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mux.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,28 @@ +libavformat/mux.o libavformat/mux.o: libavformat/mux.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h libavcodec/internal.h \ + libavutil/mathematics.h libavcodec/avcodec.h config.h \ + libavcodec/bytestream.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/opt.h \ + libavutil/pixdesc.h libavutil/timestamp.h libavformat/metadata.h \ + libavformat/id3v2.h libavutil/avassert.h libavutil/avstring.h \ + libavutil/mathematics.h libavutil/parseutils.h libavutil/time.h \ + libavformat/riff.h libavformat/audiointerleave.h libavutil/fifo.h \ + libavformat/network.h config.h libavutil/error.h \ + libavformat/os_support.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mux.o Binary file ffmpeg/libavformat/mux.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mvdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,441 @@ +/* + * Silicon Graphics Movie demuxer + * Copyright (c) 2012 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Silicon Graphics Movie demuxer + */ + +#include "libavutil/eval.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/rational.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int nb_video_tracks; + int nb_audio_tracks; + + int eof_count; /**< number of streams that have finished */ + int stream_index; /**< current stream index */ + int frame[2]; /**< frame nb for current stream */ +} MvContext; + +#define AUDIO_FORMAT_SIGNED 401 + +static int mv_probe(AVProbeData *p) +{ + if (AV_RB32(p->buf) == MKBETAG('M','O','V','I') && AV_RB16(p->buf + 4) < 3) + return AVPROBE_SCORE_MAX; + return 0; +} + +static char * var_read_string(AVIOContext *pb, int size) +{ + char *str = av_malloc(size + 1); + int n; + if (!str) + return NULL; + n = avio_get_str(pb, size, str, size + 1); + if (n < size) + avio_skip(pb, size - n); + return str; +} + +static int var_read_int(AVIOContext *pb, int size) +{ + int v; + char * s = var_read_string(pb, size); + if (!s || sscanf(s, "%d", &v) != 1) + v = 0; + av_free(s); + return v; +} + +static AVRational var_read_float(AVIOContext *pb, int size) +{ + AVRational v; + char * s = var_read_string(pb, size); + if (!s) + return (AVRational){0, 0}; + v = av_d2q(av_strtod(s, NULL), INT_MAX); + av_free(s); + return v; +} + +static void var_read_metadata(AVFormatContext *avctx, const char *tag, int size) +{ + char *value = var_read_string(avctx->pb, size); + if (value) + av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL); +} + +static int set_channels(AVFormatContext *avctx, AVStream *st, int channels) { + if (channels <= 0) { + av_log(avctx, AV_LOG_ERROR, "Channel count %d invalid\n", channels); + return AVERROR_INVALIDDATA; + } + st->codec->channels = channels; + st->codec->channel_layout = (st->codec->channels == 1) ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; + return 0; +} + +/** + * Parse global variable + * @return < 0 if unknown + */ +static int parse_global_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__NUM_I_TRACKS")) { + mv->nb_video_tracks = var_read_int(pb, size); + } else if (!strcmp(name, "__NUM_A_TRACKS")) { + mv->nb_audio_tracks = var_read_int(pb, size); + } else if (!strcmp(name, "COMMENT") || !strcmp(name, "TITLE")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "LOOP_MODE") || !strcmp(name, "NUM_LOOPS") || !strcmp(name, "OPTIMIZED")) { + avio_skip(pb, size); // ignore + } else + return -1; + + return 0; +} + +/** + * Parse audio variable + * @return < 0 if unknown + */ +static int parse_audio_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__DIR_COUNT")) { + st->nb_frames = var_read_int(pb, size); + } else if (!strcmp(name, "AUDIO_FORMAT")) { + st->codec->codec_id = var_read_int(pb, size); + } else if (!strcmp(name, "COMPRESSION")) { + st->codec->codec_tag = var_read_int(pb, size); + } else if (!strcmp(name, "DEFAULT_VOL")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "NUM_CHANNELS")) { + return set_channels(avctx, st, var_read_int(pb, size)); + } else if (!strcmp(name, "SAMPLE_RATE")) { + st->codec->sample_rate = var_read_int(pb, size); + avpriv_set_pts_info(st, 33, 1, st->codec->sample_rate); + } else if (!strcmp(name, "SAMPLE_WIDTH")) { + st->codec->bits_per_coded_sample = var_read_int(pb, size) * 8; + } else + return -1; + return 0; +} + +/** + * Parse video variable + * @return < 0 if unknown + */ +static int parse_video_var(AVFormatContext *avctx, AVStream *st, const char *name, int size) +{ + AVIOContext *pb = avctx->pb; + if (!strcmp(name, "__DIR_COUNT")) { + st->nb_frames = st->duration = var_read_int(pb, size); + } else if (!strcmp(name, "COMPRESSION")) { + char * str = var_read_string(pb, size); + if (!str) + return AVERROR_INVALIDDATA; + if (!strcmp(str, "1")) { + st->codec->codec_id = AV_CODEC_ID_MVC1; + } else if (!strcmp(str, "2")) { + st->codec->pix_fmt = AV_PIX_FMT_ABGR; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + } else if (!strcmp(str, "3")) { + st->codec->codec_id = AV_CODEC_ID_SGIRLE; + } else if (!strcmp(str, "10")) { + st->codec->codec_id = AV_CODEC_ID_MJPEG; + } else if (!strcmp(str, "MVC2")) { + st->codec->codec_id = AV_CODEC_ID_MVC2; + } else { + avpriv_request_sample(avctx, "video compression %s", str); + } + av_free(str); + } else if (!strcmp(name, "FPS")) { + AVRational fps = var_read_float(pb, size); + avpriv_set_pts_info(st, 64, fps.den, fps.num); + } else if (!strcmp(name, "HEIGHT")) { + st->codec->height = var_read_int(pb, size); + } else if (!strcmp(name, "PIXEL_ASPECT")) { + st->sample_aspect_ratio = var_read_float(pb, size); + av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, INT_MAX); + } else if (!strcmp(name, "WIDTH")) { + st->codec->width = var_read_int(pb, size); + } else if (!strcmp(name, "ORIENTATION")) { + if (var_read_int(pb, size) == 1101) { + st->codec->extradata = av_strdup("BottomUp"); + st->codec->extradata_size = 9; + } + } else if (!strcmp(name, "Q_SPATIAL") || !strcmp(name, "Q_TEMPORAL")) { + var_read_metadata(avctx, name, size); + } else if (!strcmp(name, "INTERLACING") || !strcmp(name, "PACKING")) { + avio_skip(pb, size); // ignore + } else + return -1; + return 0; +} + +static void read_table(AVFormatContext *avctx, AVStream *st, int (*parse)(AVFormatContext *avctx, AVStream *st, const char *name, int size)) +{ + int count, i; + AVIOContext *pb = avctx->pb; + avio_skip(pb, 4); + count = avio_rb32(pb); + avio_skip(pb, 4); + for (i = 0; i < count; i++) { + char name[17]; + int size; + avio_read(pb, name, 16); + name[sizeof(name) - 1] = 0; + size = avio_rb32(pb); + if (parse(avctx, st, name, size) < 0) { + avpriv_request_sample(avctx, "variable %s", name); + avio_skip(pb, size); + } + } +} + +static void read_index(AVIOContext *pb, AVStream *st) +{ + uint64_t timestamp = 0; + int i; + for (i = 0; i < st->nb_frames; i++) { + uint32_t pos = avio_rb32(pb); + uint32_t size = avio_rb32(pb); + avio_skip(pb, 8); + av_add_index_entry(st, pos, timestamp, size, 0, AVINDEX_KEYFRAME); + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + timestamp += size / (st->codec->channels * 2); + } else { + timestamp++; + } + } +} + +static int mv_read_header(AVFormatContext *avctx) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + AVStream *ast = NULL, *vst = NULL; //initialization to suppress warning + int version, i; + + avio_skip(pb, 4); + + version = avio_rb16(pb); + if (version == 2) { + uint64_t timestamp; + int v; + avio_skip(pb, 22); + + /* allocate audio track first to prevent unnecessary seeking + (audio packet always precede video packet for a given frame) */ + ast = avformat_new_stream(avctx, NULL); + if (!ast) + return AVERROR(ENOMEM); + + vst = avformat_new_stream(avctx, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avpriv_set_pts_info(vst, 64, 1, 15); + vst->nb_frames = avio_rb32(pb); + v = avio_rb32(pb); + switch (v) { + case 1: + vst->codec->codec_id = AV_CODEC_ID_MVC1; + break; + case 2: + vst->codec->pix_fmt = AV_PIX_FMT_ARGB; + vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + break; + default: + avpriv_request_sample(avctx, "video compression %i", v); + break; + } + vst->codec->codec_tag = 0; + vst->codec->width = avio_rb32(pb); + vst->codec->height = avio_rb32(pb); + avio_skip(pb, 12); + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->nb_frames = vst->nb_frames; + ast->codec->sample_rate = avio_rb32(pb); + avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate); + if (set_channels(avctx, ast, avio_rb32(pb)) < 0) + return AVERROR_INVALIDDATA; + + v = avio_rb32(pb); + if (v == AUDIO_FORMAT_SIGNED) { + ast->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else { + avpriv_request_sample(avctx, "audio compression (format %i)", v); + } + + avio_skip(pb, 12); + var_read_metadata(avctx, "title", 0x80); + var_read_metadata(avctx, "comment", 0x100); + avio_skip(pb, 0x80); + + timestamp = 0; + for (i = 0; i < vst->nb_frames; i++) { + uint32_t pos = avio_rb32(pb); + uint32_t asize = avio_rb32(pb); + uint32_t vsize = avio_rb32(pb); + avio_skip(pb, 8); + av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME); + av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME); + timestamp += asize / (ast->codec->channels * 2); + } + } else if (!version && avio_rb16(pb) == 3) { + avio_skip(pb, 4); + + read_table(avctx, NULL, parse_global_var); + + if (mv->nb_audio_tracks > 1) { + avpriv_request_sample(avctx, "multiple audio streams support"); + return AVERROR_PATCHWELCOME; + } else if (mv->nb_audio_tracks) { + ast = avformat_new_stream(avctx, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + /* temporarily store compression value in codec_tag; format value in codec_id */ + read_table(avctx, ast, parse_audio_var); + if (ast->codec->codec_tag == 100 && ast->codec->codec_id == AUDIO_FORMAT_SIGNED && ast->codec->bits_per_coded_sample == 16) { + ast->codec->codec_id = AV_CODEC_ID_PCM_S16BE; + } else { + avpriv_request_sample(avctx, "audio compression %i (format %i, width %i)", + ast->codec->codec_tag, ast->codec->codec_id, ast->codec->bits_per_coded_sample); + ast->codec->codec_id = AV_CODEC_ID_NONE; + } + ast->codec->codec_tag = 0; + if (ast->codec->channels <= 0) { + av_log(avctx, AV_LOG_ERROR, "No valid channel count found\n"); + return AVERROR_INVALIDDATA; + } + } + + if (mv->nb_video_tracks > 1) { + avpriv_request_sample(avctx, "multiple video streams support"); + return AVERROR_PATCHWELCOME; + } else if (mv->nb_video_tracks) { + vst = avformat_new_stream(avctx, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + read_table(avctx, vst, parse_video_var); + } + + if (mv->nb_audio_tracks) + read_index(pb, ast); + + if (mv->nb_video_tracks) + read_index(pb, vst); + } else { + avpriv_request_sample(avctx, "version %i", version); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int mv_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + MvContext *mv = avctx->priv_data; + AVIOContext *pb = avctx->pb; + AVStream *st = avctx->streams[mv->stream_index]; + const AVIndexEntry *index; + int frame = mv->frame[mv->stream_index]; + int ret; + uint64_t pos; + + if (frame < st->nb_index_entries) { + index = &st->index_entries[frame]; + pos = avio_tell(pb); + if (index->pos > pos) + avio_skip(pb, index->pos - pos); + else if (index->pos < pos) { + if (!pb->seekable) + return AVERROR(EIO); + ret = avio_seek(pb, index->pos, SEEK_SET); + if (ret < 0) + return ret; + } + ret = av_get_packet(pb, pkt, index->size); + if (ret < 0) + return ret; + + pkt->stream_index = mv->stream_index; + pkt->pts = index->timestamp; + pkt->flags |= AV_PKT_FLAG_KEY; + + mv->frame[mv->stream_index]++; + mv->eof_count = 0; + } else { + mv->eof_count++; + if (mv->eof_count >= avctx->nb_streams) + return AVERROR_EOF; + } + + mv->stream_index++; + if (mv->stream_index >= avctx->nb_streams) + mv->stream_index = 0; + + return 0; +} + +static int mv_read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags) +{ + MvContext *mv = avctx->priv_data; + AVStream *st = avctx->streams[stream_index]; + int frame, i; + + if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE)) + return AVERROR(ENOSYS); + + if (!avctx->pb->seekable) + return AVERROR(EIO); + + frame = av_index_search_timestamp(st, timestamp, flags); + if (frame < 0) + return -1; + + for (i = 0; i < avctx->nb_streams; i++) + mv->frame[i] = frame; + return 0; +} + +AVInputFormat ff_mv_demuxer = { + .name = "mv", + .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), + .priv_data_size = sizeof(MvContext), + .read_probe = mv_probe, + .read_header = mv_read_header, + .read_packet = mv_read_packet, + .read_seek = mv_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mvdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/mvdec.o libavformat/mvdec.o: libavformat/mvdec.c \ + libavutil/eval.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/rational.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvdec.o Binary file ffmpeg/libavformat/mvdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mvi.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,143 @@ +/* + * Motion Pixels MVI Demuxer + * Copyright (c) 2008 Gregory Montoir (cyx@users.sourceforge.net) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" + +#define MVI_FRAC_BITS 10 + +#define MVI_AUDIO_STREAM_INDEX 0 +#define MVI_VIDEO_STREAM_INDEX 1 + +typedef struct MviDemuxContext { + unsigned int (*get_int)(AVIOContext *); + uint32_t audio_data_size; + uint64_t audio_size_counter; + uint64_t audio_frame_size; + int audio_size_left; + int video_frame_size; +} MviDemuxContext; + +static int read_header(AVFormatContext *s) +{ + MviDemuxContext *mvi = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast, *vst; + unsigned int version, frames_count, msecs_per_frame, player_version; + + ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + + vst->codec->extradata_size = 2; + vst->codec->extradata = av_mallocz(2 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + + version = avio_r8(pb); + vst->codec->extradata[0] = avio_r8(pb); + vst->codec->extradata[1] = avio_r8(pb); + frames_count = avio_rl32(pb); + msecs_per_frame = avio_rl32(pb); + vst->codec->width = avio_rl16(pb); + vst->codec->height = avio_rl16(pb); + avio_r8(pb); + ast->codec->sample_rate = avio_rl16(pb); + mvi->audio_data_size = avio_rl32(pb); + avio_r8(pb); + player_version = avio_rl32(pb); + avio_rl16(pb); + avio_r8(pb); + + if (frames_count == 0 || mvi->audio_data_size == 0) + return AVERROR_INVALIDDATA; + + if (version != 7 || player_version > 213) { + av_log(s, AV_LOG_ERROR, "unhandled version (%d,%d)\n", version, player_version); + return AVERROR_INVALIDDATA; + } + + avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_PCM_U8; + ast->codec->channels = 1; + ast->codec->channel_layout = AV_CH_LAYOUT_MONO; + ast->codec->bits_per_coded_sample = 8; + ast->codec->bit_rate = ast->codec->sample_rate * 8; + + avpriv_set_pts_info(vst, 64, msecs_per_frame, 1000000); + vst->avg_frame_rate = av_inv_q(vst->time_base); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_MOTIONPIXELS; + + mvi->get_int = (vst->codec->width * vst->codec->height < (1 << 16)) ? avio_rl16 : avio_rl24; + + mvi->audio_frame_size = ((uint64_t)mvi->audio_data_size << MVI_FRAC_BITS) / frames_count; + if (!mvi->audio_frame_size) { + av_log(s, AV_LOG_ERROR, "audio_frame_size is 0\n"); + return AVERROR_INVALIDDATA; + } + mvi->audio_size_counter = (ast->codec->sample_rate * 830 / mvi->audio_frame_size - 1) * mvi->audio_frame_size; + mvi->audio_size_left = mvi->audio_data_size; + + return 0; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, count; + MviDemuxContext *mvi = s->priv_data; + AVIOContext *pb = s->pb; + + if (mvi->video_frame_size == 0) { + mvi->video_frame_size = (mvi->get_int)(pb); + if (mvi->audio_size_left == 0) + return AVERROR(EIO); + count = (mvi->audio_size_counter + mvi->audio_frame_size + 512) >> MVI_FRAC_BITS; + if (count > mvi->audio_size_left) + count = mvi->audio_size_left; + if ((ret = av_get_packet(pb, pkt, count)) < 0) + return ret; + pkt->stream_index = MVI_AUDIO_STREAM_INDEX; + mvi->audio_size_left -= count; + mvi->audio_size_counter += mvi->audio_frame_size - (count << MVI_FRAC_BITS); + } else { + if ((ret = av_get_packet(pb, pkt, mvi->video_frame_size)) < 0) + return ret; + pkt->stream_index = MVI_VIDEO_STREAM_INDEX; + mvi->video_frame_size = 0; + } + return 0; +} + +AVInputFormat ff_mvi_demuxer = { + .name = "mvi", + .long_name = NULL_IF_CONFIG_SMALL("Motion Pixels MVI"), + .priv_data_size = sizeof(MviDemuxContext), + .read_header = read_header, + .read_packet = read_packet, + .extensions = "mvi", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvi.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mvi.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/mvi.o libavformat/mvi.o: libavformat/mvi.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mvi.o Binary file ffmpeg/libavformat/mvi.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,137 @@ +/* + * MXF + * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" +#include "mxf.h" + +/** + * SMPTE RP224 http://www.smpte-ra.org/mdd/index.html + */ +const MXFCodecUL ff_mxf_data_definition_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x01,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_VIDEO }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x02,0x00,0x00,0x00 }, 13, AVMEDIA_TYPE_AUDIO }, + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AVMEDIA_TYPE_DATA }, +}; + +const MXFCodecUL ff_mxf_codec_uls[] = { + /* PictureEssenceCoding */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MP@ML Long GoP */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* D-10 50Mbps PAL */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x03,0x03,0x00 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MP@HL Long GoP */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x04,0x02,0x00 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* 422P@HL I-Frame */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x20,0x02,0x03 }, 14, AV_CODEC_ID_MPEG4 }, /* XDCAM proxy_pal030926.mxf */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 }, 13, AV_CODEC_ID_DVVIDEO }, /* DV25 IEC PAL */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x04,0x01,0x02,0x02,0x03,0x01,0x01,0x00 }, 14, AV_CODEC_ID_JPEG2000 }, /* JPEG2000 Codestream */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, 15, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed 422 8-bit */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x71,0x00,0x00,0x00 }, 13, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x03,0x02,0x00,0x00 }, 14, AV_CODEC_ID_DNXHD }, /* SMPTE VC-3/DNxHD */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x02,0x01 }, 16, AV_CODEC_ID_V210 }, /* V210 */ + /* SoundEssenceCompression */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, /* Uncompressed */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x7F,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16LE }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x04,0x02,0x02,0x01,0x7E,0x00,0x00,0x00 }, 13, AV_CODEC_ID_PCM_S16BE }, /* From Omneon MXF file */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x04,0x04,0x02,0x02,0x02,0x03,0x01,0x01,0x00 }, 15, AV_CODEC_ID_PCM_ALAW }, /* XDCAM Proxy C0023S01.mxf */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x01,0x00 }, 15, AV_CODEC_ID_AC3 }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x05,0x00 }, 15, AV_CODEC_ID_MP2 }, /* MP2 or MP3 */ + //{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x1C,0x00 }, 15, AV_CODEC_ID_DOLBY_E }, /* Dolby-E */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +const MXFCodecUL ff_mxf_pixel_format_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x01 }, 16, AV_PIX_FMT_UYVY422 }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x01,0x01,0x02,0x01,0x02 }, 16, AV_PIX_FMT_YUYV422 }, + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_PIX_FMT_NONE }, +}; + +static const struct { + enum AVPixelFormat pix_fmt; + const char data[16]; +} ff_mxf_pixel_layouts[] = { + /** + * See SMPTE 377M E.2.46 + * + * Note: Only RGB, palette based and "abnormal" YUV pixel formats like 4:2:2:4 go here. + * For regular YUV, use CDCIPictureEssenceDescriptor. + * + * Note: Do not use these for encoding descriptors for little-endian formats until we + * get samples or official word from SMPTE on how/if those can be encoded. + */ + {AV_PIX_FMT_ABGR, {'A', 8, 'B', 8, 'G', 8, 'R', 8 }}, + {AV_PIX_FMT_ARGB, {'A', 8, 'R', 8, 'G', 8, 'B', 8 }}, + {AV_PIX_FMT_BGR24, {'B', 8, 'G', 8, 'R', 8 }}, + {AV_PIX_FMT_BGRA, {'B', 8, 'G', 8, 'R', 8, 'A', 8 }}, + {AV_PIX_FMT_RGB24, {'R', 8, 'G', 8, 'B', 8 }}, + {AV_PIX_FMT_RGB444BE,{'F', 4, 'R', 4, 'G', 4, 'B', 4 }}, + {AV_PIX_FMT_RGB48BE, {'R', 8, 'r', 8, 'G', 8, 'g', 8, 'B', 8, 'b', 8 }}, + {AV_PIX_FMT_RGB48BE, {'R', 16, 'G', 16, 'B', 16 }}, + {AV_PIX_FMT_RGB48LE, {'r', 8, 'R', 8, 'g', 8, 'G', 8, 'b', 8, 'B', 8 }}, + {AV_PIX_FMT_RGB555BE,{'F', 1, 'R', 5, 'G', 5, 'B', 5 }}, + {AV_PIX_FMT_RGB565BE,{'R', 5, 'G', 6, 'B', 5 }}, + {AV_PIX_FMT_RGBA, {'R', 8, 'G', 8, 'B', 8, 'A', 8 }}, + {AV_PIX_FMT_PAL8, {'P', 8 }}, +}; + +static const int num_pixel_layouts = FF_ARRAY_ELEMS(ff_mxf_pixel_layouts); + +int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt) +{ + int x; + + for(x = 0; x < num_pixel_layouts; x++) { + if (!memcmp(pixel_layout, ff_mxf_pixel_layouts[x].data, 16)) { + *pix_fmt = ff_mxf_pixel_layouts[x].pix_fmt; + return 0; + } + } + + return -1; +} + +static const MXFSamplesPerFrame mxf_samples_per_frames[] = { + { { 1001, 24000 }, { 2002, 0, 0, 0, 0, 0 } }, // FILM 23.976 + { { 1, 24}, { 2000, 0, 0, 0, 0, 0 } }, // FILM 24 + { { 1001, 30000 }, { 1602, 1601, 1602, 1601, 1602, 0 } }, // NTSC 29.97 + { { 1001, 60000 }, { 801, 801, 801, 801, 800, 0 } }, // NTSC 59.94 + { { 1, 25 }, { 1920, 0, 0, 0, 0, 0 } }, // PAL 25 + { { 1, 50 }, { 960, 0, 0, 0, 0, 0 } }, // PAL 50 +}; + +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (!av_cmp_q(mxf_samples_per_frames[i].time_base, time_base)) + return &mxf_samples_per_frames[i]; + } + + // Find closest container time base for approximative codec time base like 1/29.97, 1/30, ... + for (i = 0; i < FF_ARRAY_ELEMS(mxf_samples_per_frames); i++) { + if (fabs(av_q2d(mxf_samples_per_frames[i].time_base) - av_q2d(time_base)) < 0.0001) { + av_log(s, AV_LOG_WARNING, "%d/%d input time base matched %d/%d container time base\n", + time_base.num, time_base.den, + mxf_samples_per_frames[i].time_base.num, mxf_samples_per_frames[i].time_base.den); + return &mxf_samples_per_frames[i]; + } + } + return NULL; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/mxf.o libavformat/mxf.o: libavformat/mxf.c libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavformat/mxf.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxf.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,85 @@ +/* + * MXF + * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVFORMAT_MXF_H +#define AVFORMAT_MXF_H + +#include "avformat.h" +#include "libavcodec/avcodec.h" +#include + +typedef uint8_t UID[16]; + +enum MXFMetadataSetType { + AnyType, + MaterialPackage, + SourcePackage, + SourceClip, + TimecodeComponent, + Sequence, + MultipleDescriptor, + Descriptor, + Track, + CryptoContext, + Preface, + Identification, + ContentStorage, + SubDescriptor, + IndexTableSegment, + EssenceContainerData, + TypeBottom,// add metadata type before this +}; + +enum MXFFrameLayout { + FullFrame = 0, + SeparateFields, + OneField, + MixedFields, + SegmentedFrame, +}; + +typedef struct KLVPacket { + UID key; + int64_t offset; + uint64_t length; +} KLVPacket; + +typedef struct MXFCodecUL { + UID uid; + unsigned matching_len; + int id; +} MXFCodecUL; + +typedef struct { + struct AVRational time_base; + int samples_per_frame[6]; +} MXFSamplesPerFrame; + +extern const MXFCodecUL ff_mxf_data_definition_uls[]; +extern const MXFCodecUL ff_mxf_codec_uls[]; +extern const MXFCodecUL ff_mxf_pixel_format_uls[]; + +int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt); +const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base); + +#define PRINT_KEY(pc, s, x) av_dlog(pc, "%s %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", s, \ + (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5], (x)[6], (x)[7], (x)[8], (x)[9], (x)[10], (x)[11], (x)[12], (x)[13], (x)[14], (x)[15]) + +#endif /* AVFORMAT_MXF_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxf.o Binary file ffmpeg/libavformat/mxf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,2513 @@ +/* + * MXF demuxer. + * Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * References + * SMPTE 336M KLV Data Encoding Protocol Using Key-Length-Value + * SMPTE 377M MXF File Format Specifications + * SMPTE 378M Operational Pattern 1a + * SMPTE 379M MXF Generic Container + * SMPTE 381M Mapping MPEG Streams into the MXF Generic Container + * SMPTE 382M Mapping AES3 and Broadcast Wave Audio into the MXF Generic Container + * SMPTE 383M Mapping DV-DIF Data to the MXF Generic Container + * + * Principle + * Search for Track numbers which will identify essence element KLV packets. + * Search for SourcePackage which define tracks which contains Track numbers. + * Material Package contains tracks with reference to SourcePackage tracks. + * Search for Descriptors (Picture, Sound) which contains codec info and parameters. + * Assign Descriptors to correct Tracks. + * + * Metadata reading functions read Local Tags, get InstanceUID(0x3C0A) then add MetaDataSet to MXFContext. + * Metadata parsing resolves Strong References to objects. + * + * Simple demuxer, only OP1A supported and some files might not work at all. + * Only tracks with associated descriptors will be decoded. "Highly Desirable" SMPTE 377M D.1 + */ + +//#define DEBUG + +#include "libavutil/aes.h" +#include "libavutil/avassert.h" +#include "libavutil/mathematics.h" +#include "libavcodec/bytestream.h" +#include "libavutil/timecode.h" +#include "avformat.h" +#include "internal.h" +#include "mxf.h" + +typedef enum { + Header, + BodyPartition, + Footer +} MXFPartitionType; + +typedef enum { + OP1a = 1, + OP1b, + OP1c, + OP2a, + OP2b, + OP2c, + OP3a, + OP3b, + OP3c, + OPAtom, + OPSONYOpt, /* FATE sample, violates the spec in places */ +} MXFOP; + +typedef struct { + int closed; + int complete; + MXFPartitionType type; + uint64_t previous_partition; + int index_sid; + int body_sid; + int64_t this_partition; + int64_t essence_offset; ///< absolute offset of essence + int64_t essence_length; + int32_t kag_size; + int64_t header_byte_count; + int64_t index_byte_count; + int pack_length; +} MXFPartition; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID source_container_ul; +} MXFCryptoContext; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID source_package_uid; + UID data_definition_ul; + int64_t duration; + int64_t start_position; + int source_track_id; +} MXFStructuralComponent; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID data_definition_ul; + UID *structural_components_refs; + int structural_components_count; + int64_t duration; +} MXFSequence; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + int drop_frame; + int start_frame; + struct AVRational rate; + AVTimecode tc; +} MXFTimecodeComponent; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + MXFSequence *sequence; /* mandatory, and only one */ + UID sequence_ref; + int track_id; + uint8_t track_number[4]; + AVRational edit_rate; + int intra_only; + uint64_t sample_count; + int64_t original_duration; ///< duration before multiplying st->duration by SampleRate/EditRate +} MXFTrack; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID essence_container_ul; + UID essence_codec_ul; + AVRational sample_rate; + AVRational aspect_ratio; + int width; + int height; /* Field height, not frame height */ + int frame_layout; /* See MXFFrameLayout enum */ + int channels; + int bits_per_sample; + int field_dominance; + unsigned int component_depth; + unsigned int horiz_subsampling; + unsigned int vert_subsampling; + UID *sub_descriptors_refs; + int sub_descriptors_count; + int linked_track_id; + uint8_t *extradata; + int extradata_size; + enum AVPixelFormat pix_fmt; +} MXFDescriptor; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + int edit_unit_byte_count; + int index_sid; + int body_sid; + AVRational index_edit_rate; + uint64_t index_start_position; + uint64_t index_duration; + int8_t *temporal_offset_entries; + int *flag_entries; + uint64_t *stream_offset_entries; + int nb_index_entries; +} MXFIndexTableSegment; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; + UID package_uid; + UID *tracks_refs; + int tracks_count; + MXFDescriptor *descriptor; /* only one */ + UID descriptor_ref; +} MXFPackage; + +typedef struct { + UID uid; + enum MXFMetadataSetType type; +} MXFMetadataSet; + +/* decoded index table */ +typedef struct { + int index_sid; + int body_sid; + int nb_ptses; /* number of PTSes or total duration of index */ + int64_t first_dts; /* DTS = EditUnit + first_dts */ + int64_t *ptses; /* maps EditUnit -> PTS */ + int nb_segments; + MXFIndexTableSegment **segments; /* sorted by IndexStartPosition */ + AVIndexEntry *fake_index; /* used for calling ff_index_search_timestamp() */ +} MXFIndexTable; + +typedef struct { + MXFPartition *partitions; + unsigned partitions_count; + MXFOP op; + UID *packages_refs; + int packages_count; + MXFMetadataSet **metadata_sets; + int metadata_sets_count; + AVFormatContext *fc; + struct AVAES *aesc; + uint8_t *local_tags; + int local_tags_count; + uint64_t footer_partition; + KLVPacket current_klv_data; + int current_klv_index; + int run_in; + MXFPartition *current_partition; + int parsing_backward; + int64_t last_forward_tell; + int last_forward_partition; + int current_edit_unit; + int nb_index_tables; + MXFIndexTable *index_tables; + int edit_units_per_packet; ///< how many edit units to read at a time (PCM, OPAtom) +} MXFContext; + +enum MXFWrappingScheme { + Frame, + Clip, +}; + +/* NOTE: klv_offset is not set (-1) for local keys */ +typedef int MXFMetadataReadFunc(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset); + +typedef struct { + const UID key; + MXFMetadataReadFunc *read; + int ctx_size; + enum MXFMetadataSetType type; +} MXFMetadataReadTableEntry; + +static int mxf_read_close(AVFormatContext *s); + +/* partial keys to match */ +static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 }; +static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 }; +static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 }; +static const uint8_t mxf_system_item_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04 }; +static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 }; +/* complete keys to match */ +static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 }; +static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 }; +static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 }; +static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 }; + +#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y))) + +static int64_t klv_decode_ber_length(AVIOContext *pb) +{ + uint64_t size = avio_r8(pb); + if (size & 0x80) { /* long form */ + int bytes_num = size & 0x7f; + /* SMPTE 379M 5.3.4 guarantee that bytes_num must not exceed 8 bytes */ + if (bytes_num > 8) + return AVERROR_INVALIDDATA; + size = 0; + while (bytes_num--) + size = size << 8 | avio_r8(pb); + } + return size; +} + +static int mxf_read_sync(AVIOContext *pb, const uint8_t *key, unsigned size) +{ + int i, b; + for (i = 0; i < size && !url_feof(pb); i++) { + b = avio_r8(pb); + if (b == key[0]) + i = 0; + else if (b != key[i]) + i = -1; + } + return i == size; +} + +static int klv_read_packet(KLVPacket *klv, AVIOContext *pb) +{ + if (!mxf_read_sync(pb, mxf_klv_key, 4)) + return AVERROR_INVALIDDATA; + klv->offset = avio_tell(pb) - 4; + memcpy(klv->key, mxf_klv_key, 4); + avio_read(pb, klv->key + 4, 12); + klv->length = klv_decode_ber_length(pb); + return klv->length == -1 ? -1 : 0; +} + +static int mxf_get_stream_index(AVFormatContext *s, KLVPacket *klv) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + MXFTrack *track = s->streams[i]->priv_data; + /* SMPTE 379M 7.3 */ + if (!memcmp(klv->key + sizeof(mxf_essence_element_key), track->track_number, sizeof(track->track_number))) + return i; + } + /* return 0 if only one stream, for OP Atom files with 0 as track number */ + return s->nb_streams == 1 ? 0 : -1; +} + +/* XXX: use AVBitStreamFilter */ +static int mxf_get_d10_aes3_packet(AVIOContext *pb, AVStream *st, AVPacket *pkt, int64_t length) +{ + const uint8_t *buf_ptr, *end_ptr; + uint8_t *data_ptr; + int i; + + if (length > 61444) /* worst case PAL 1920 samples 8 channels */ + return AVERROR_INVALIDDATA; + length = av_get_packet(pb, pkt, length); + if (length < 0) + return length; + data_ptr = pkt->data; + end_ptr = pkt->data + length; + buf_ptr = pkt->data + 4; /* skip SMPTE 331M header */ + for (; buf_ptr + st->codec->channels*4 <= end_ptr; ) { + for (i = 0; i < st->codec->channels; i++) { + uint32_t sample = bytestream_get_le32(&buf_ptr); + if (st->codec->bits_per_coded_sample == 24) + bytestream_put_le24(&data_ptr, (sample >> 4) & 0xffffff); + else + bytestream_put_le16(&data_ptr, (sample >> 12) & 0xffff); + } + buf_ptr += 32 - st->codec->channels*4; // always 8 channels stored SMPTE 331M + } + av_shrink_packet(pkt, data_ptr - pkt->data); + return 0; +} + +static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv) +{ + static const uint8_t checkv[16] = {0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b}; + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t end = avio_tell(pb) + klv->length; + int64_t size; + uint64_t orig_size; + uint64_t plaintext_size; + uint8_t ivec[16]; + uint8_t tmpbuf[16]; + int index; + + if (!mxf->aesc && s->key && s->keylen == 16) { + mxf->aesc = av_aes_alloc(); + if (!mxf->aesc) + return AVERROR(ENOMEM); + av_aes_init(mxf->aesc, s->key, 128, 1); + } + // crypto context + avio_skip(pb, klv_decode_ber_length(pb)); + // plaintext offset + klv_decode_ber_length(pb); + plaintext_size = avio_rb64(pb); + // source klv key + klv_decode_ber_length(pb); + avio_read(pb, klv->key, 16); + if (!IS_KLV_KEY(klv, mxf_essence_element_key)) + return AVERROR_INVALIDDATA; + index = mxf_get_stream_index(s, klv); + if (index < 0) + return AVERROR_INVALIDDATA; + // source size + klv_decode_ber_length(pb); + orig_size = avio_rb64(pb); + if (orig_size < plaintext_size) + return AVERROR_INVALIDDATA; + // enc. code + size = klv_decode_ber_length(pb); + if (size < 32 || size - 32 < orig_size) + return AVERROR_INVALIDDATA; + avio_read(pb, ivec, 16); + avio_read(pb, tmpbuf, 16); + if (mxf->aesc) + av_aes_crypt(mxf->aesc, tmpbuf, tmpbuf, 1, ivec, 1); + if (memcmp(tmpbuf, checkv, 16)) + av_log(s, AV_LOG_ERROR, "probably incorrect decryption key\n"); + size -= 32; + size = av_get_packet(pb, pkt, size); + if (size < 0) + return size; + else if (size < plaintext_size) + return AVERROR_INVALIDDATA; + size -= plaintext_size; + if (mxf->aesc) + av_aes_crypt(mxf->aesc, &pkt->data[plaintext_size], + &pkt->data[plaintext_size], size >> 4, ivec, 1); + av_shrink_packet(pkt, orig_size); + pkt->stream_index = index; + avio_skip(pb, end - avio_tell(pb)); + return 0; +} + +static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFContext *mxf = arg; + int item_num = avio_rb32(pb); + int item_len = avio_rb32(pb); + + if (item_len != 18) { + avpriv_request_sample(pb, "Primer pack item length %d", item_len); + return AVERROR_PATCHWELCOME; + } + if (item_num > 65536) { + av_log(mxf->fc, AV_LOG_ERROR, "item_num %d is too large\n", item_num); + return AVERROR_INVALIDDATA; + } + mxf->local_tags = av_calloc(item_num, item_len); + if (!mxf->local_tags) + return AVERROR(ENOMEM); + mxf->local_tags_count = item_num; + avio_read(pb, mxf->local_tags, item_num*item_len); + return 0; +} + +static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFContext *mxf = arg; + MXFPartition *partition, *tmp_part; + UID op; + uint64_t footer_partition; + uint32_t nb_essence_containers; + + if (mxf->partitions_count+1 >= UINT_MAX / sizeof(*mxf->partitions)) + return AVERROR(ENOMEM); + + tmp_part = av_realloc(mxf->partitions, (mxf->partitions_count + 1) * sizeof(*mxf->partitions)); + if (!tmp_part) + return AVERROR(ENOMEM); + mxf->partitions = tmp_part; + + if (mxf->parsing_backward) { + /* insert the new partition pack in the middle + * this makes the entries in mxf->partitions sorted by offset */ + memmove(&mxf->partitions[mxf->last_forward_partition+1], + &mxf->partitions[mxf->last_forward_partition], + (mxf->partitions_count - mxf->last_forward_partition)*sizeof(*mxf->partitions)); + partition = mxf->current_partition = &mxf->partitions[mxf->last_forward_partition]; + } else { + mxf->last_forward_partition++; + partition = mxf->current_partition = &mxf->partitions[mxf->partitions_count]; + } + + memset(partition, 0, sizeof(*partition)); + mxf->partitions_count++; + partition->pack_length = avio_tell(pb) - klv_offset + size; + + switch(uid[13]) { + case 2: + partition->type = Header; + break; + case 3: + partition->type = BodyPartition; + break; + case 4: + partition->type = Footer; + break; + default: + av_log(mxf->fc, AV_LOG_ERROR, "unknown partition type %i\n", uid[13]); + return AVERROR_INVALIDDATA; + } + + /* consider both footers to be closed (there is only Footer and CompleteFooter) */ + partition->closed = partition->type == Footer || !(uid[14] & 1); + partition->complete = uid[14] > 2; + avio_skip(pb, 4); + partition->kag_size = avio_rb32(pb); + partition->this_partition = avio_rb64(pb); + partition->previous_partition = avio_rb64(pb); + footer_partition = avio_rb64(pb); + partition->header_byte_count = avio_rb64(pb); + partition->index_byte_count = avio_rb64(pb); + partition->index_sid = avio_rb32(pb); + avio_skip(pb, 8); + partition->body_sid = avio_rb32(pb); + avio_read(pb, op, sizeof(UID)); + nb_essence_containers = avio_rb32(pb); + + /* some files don'thave FooterPartition set in every partition */ + if (footer_partition) { + if (mxf->footer_partition && mxf->footer_partition != footer_partition) { + av_log(mxf->fc, AV_LOG_ERROR, + "inconsistent FooterPartition value: %"PRIu64" != %"PRIu64"\n", + mxf->footer_partition, footer_partition); + } else { + mxf->footer_partition = footer_partition; + } + } + + av_dlog(mxf->fc, + "PartitionPack: ThisPartition = 0x%"PRIX64 + ", PreviousPartition = 0x%"PRIX64", " + "FooterPartition = 0x%"PRIX64", IndexSID = %i, BodySID = %i\n", + partition->this_partition, + partition->previous_partition, footer_partition, + partition->index_sid, partition->body_sid); + + /* sanity check PreviousPartition if set */ + if (partition->previous_partition && + mxf->run_in + partition->previous_partition >= klv_offset) { + av_log(mxf->fc, AV_LOG_ERROR, + "PreviousPartition points to this partition or forward\n"); + return AVERROR_INVALIDDATA; + } + + if (op[12] == 1 && op[13] == 1) mxf->op = OP1a; + else if (op[12] == 1 && op[13] == 2) mxf->op = OP1b; + else if (op[12] == 1 && op[13] == 3) mxf->op = OP1c; + else if (op[12] == 2 && op[13] == 1) mxf->op = OP2a; + else if (op[12] == 2 && op[13] == 2) mxf->op = OP2b; + else if (op[12] == 2 && op[13] == 3) mxf->op = OP2c; + else if (op[12] == 3 && op[13] == 1) mxf->op = OP3a; + else if (op[12] == 3 && op[13] == 2) mxf->op = OP3b; + else if (op[12] == 3 && op[13] == 3) mxf->op = OP3c; + else if (op[12] == 64&& op[13] == 1) mxf->op = OPSONYOpt; + else if (op[12] == 0x10) { + /* SMPTE 390m: "There shall be exactly one essence container" + * The following block deals with files that violate this, namely: + * 2011_DCPTEST_24FPS.V.mxf - two ECs, OP1a + * abcdefghiv016f56415e.mxf - zero ECs, OPAtom, output by Avid AirSpeed */ + if (nb_essence_containers != 1) { + MXFOP op = nb_essence_containers ? OP1a : OPAtom; + + /* only nag once */ + if (!mxf->op) + av_log(mxf->fc, AV_LOG_WARNING, "\"OPAtom\" with %u ECs - assuming %s\n", + nb_essence_containers, op == OP1a ? "OP1a" : "OPAtom"); + + mxf->op = op; + } else + mxf->op = OPAtom; + } else { + av_log(mxf->fc, AV_LOG_ERROR, "unknown operational pattern: %02xh %02xh - guessing OP1a\n", op[12], op[13]); + mxf->op = OP1a; + } + + if (partition->kag_size <= 0 || partition->kag_size > (1 << 20)) { + av_log(mxf->fc, AV_LOG_WARNING, "invalid KAGSize %i - guessing ", partition->kag_size); + + if (mxf->op == OPSONYOpt) + partition->kag_size = 512; + else + partition->kag_size = 1; + + av_log(mxf->fc, AV_LOG_WARNING, "%i\n", partition->kag_size); + } + + return 0; +} + +static int mxf_add_metadata_set(MXFContext *mxf, void *metadata_set) +{ + MXFMetadataSet **tmp; + if (mxf->metadata_sets_count+1 >= UINT_MAX / sizeof(*mxf->metadata_sets)) + return AVERROR(ENOMEM); + tmp = av_realloc(mxf->metadata_sets, (mxf->metadata_sets_count + 1) * sizeof(*mxf->metadata_sets)); + if (!tmp) + return AVERROR(ENOMEM); + mxf->metadata_sets = tmp; + mxf->metadata_sets[mxf->metadata_sets_count] = metadata_set; + mxf->metadata_sets_count++; + return 0; +} + +static int mxf_read_cryptographic_context(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFCryptoContext *cryptocontext = arg; + if (size != 16) + return AVERROR_INVALIDDATA; + if (IS_KLV_KEY(uid, mxf_crypto_source_container_ul)) + avio_read(pb, cryptocontext->source_container_ul, 16); + return 0; +} + +static int mxf_read_content_storage(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFContext *mxf = arg; + switch (tag) { + case 0x1901: + mxf->packages_count = avio_rb32(pb); + mxf->packages_refs = av_calloc(mxf->packages_count, sizeof(UID)); + if (!mxf->packages_refs) + return AVERROR(ENOMEM); + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)mxf->packages_refs, mxf->packages_count * sizeof(UID)); + break; + } + return 0; +} + +static int mxf_read_source_clip(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFStructuralComponent *source_clip = arg; + switch(tag) { + case 0x0202: + source_clip->duration = avio_rb64(pb); + break; + case 0x1201: + source_clip->start_position = avio_rb64(pb); + break; + case 0x1101: + /* UMID, only get last 16 bytes */ + avio_skip(pb, 16); + avio_read(pb, source_clip->source_package_uid, 16); + break; + case 0x1102: + source_clip->source_track_id = avio_rb32(pb); + break; + } + return 0; +} + +static int mxf_read_material_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFPackage *package = arg; + switch(tag) { + case 0x4403: + package->tracks_count = avio_rb32(pb); + package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID)); + if (!package->tracks_refs) + return AVERROR(ENOMEM); + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID)); + break; + } + return 0; +} + +static int mxf_read_timecode_component(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFTimecodeComponent *mxf_timecode = arg; + switch(tag) { + case 0x1501: + mxf_timecode->start_frame = avio_rb64(pb); + break; + case 0x1502: + mxf_timecode->rate = (AVRational){avio_rb16(pb), 1}; + break; + case 0x1503: + mxf_timecode->drop_frame = avio_r8(pb); + break; + } + return 0; +} + +static int mxf_read_track(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFTrack *track = arg; + switch(tag) { + case 0x4801: + track->track_id = avio_rb32(pb); + break; + case 0x4804: + avio_read(pb, track->track_number, 4); + break; + case 0x4B01: + track->edit_rate.num = avio_rb32(pb); + track->edit_rate.den = avio_rb32(pb); + break; + case 0x4803: + avio_read(pb, track->sequence_ref, 16); + break; + } + return 0; +} + +static int mxf_read_sequence(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFSequence *sequence = arg; + switch(tag) { + case 0x0202: + sequence->duration = avio_rb64(pb); + break; + case 0x0201: + avio_read(pb, sequence->data_definition_ul, 16); + break; + case 0x1001: + sequence->structural_components_count = avio_rb32(pb); + sequence->structural_components_refs = av_calloc(sequence->structural_components_count, sizeof(UID)); + if (!sequence->structural_components_refs) + return AVERROR(ENOMEM); + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)sequence->structural_components_refs, sequence->structural_components_count * sizeof(UID)); + break; + } + return 0; +} + +static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFPackage *package = arg; + switch(tag) { + case 0x4403: + package->tracks_count = avio_rb32(pb); + package->tracks_refs = av_calloc(package->tracks_count, sizeof(UID)); + if (!package->tracks_refs) + return AVERROR(ENOMEM); + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)package->tracks_refs, package->tracks_count * sizeof(UID)); + break; + case 0x4401: + /* UMID, only get last 16 bytes */ + avio_skip(pb, 16); + avio_read(pb, package->package_uid, 16); + break; + case 0x4701: + avio_read(pb, package->descriptor_ref, 16); + break; + } + return 0; +} + +static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment) +{ + int i, length; + + segment->nb_index_entries = avio_rb32(pb); + + length = avio_rb32(pb); + + if (!(segment->temporal_offset_entries=av_calloc(segment->nb_index_entries, sizeof(*segment->temporal_offset_entries))) || + !(segment->flag_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->flag_entries))) || + !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries)))) + return AVERROR(ENOMEM); + + for (i = 0; i < segment->nb_index_entries; i++) { + segment->temporal_offset_entries[i] = avio_r8(pb); + avio_r8(pb); /* KeyFrameOffset */ + segment->flag_entries[i] = avio_r8(pb); + segment->stream_offset_entries[i] = avio_rb64(pb); + avio_skip(pb, length - 11); + } + return 0; +} + +static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFIndexTableSegment *segment = arg; + switch(tag) { + case 0x3F05: + segment->edit_unit_byte_count = avio_rb32(pb); + av_dlog(NULL, "EditUnitByteCount %d\n", segment->edit_unit_byte_count); + break; + case 0x3F06: + segment->index_sid = avio_rb32(pb); + av_dlog(NULL, "IndexSID %d\n", segment->index_sid); + break; + case 0x3F07: + segment->body_sid = avio_rb32(pb); + av_dlog(NULL, "BodySID %d\n", segment->body_sid); + break; + case 0x3F0A: + av_dlog(NULL, "IndexEntryArray found\n"); + return mxf_read_index_entry_array(pb, segment); + case 0x3F0B: + segment->index_edit_rate.num = avio_rb32(pb); + segment->index_edit_rate.den = avio_rb32(pb); + av_dlog(NULL, "IndexEditRate %d/%d\n", segment->index_edit_rate.num, + segment->index_edit_rate.den); + break; + case 0x3F0C: + segment->index_start_position = avio_rb64(pb); + av_dlog(NULL, "IndexStartPosition %"PRId64"\n", segment->index_start_position); + break; + case 0x3F0D: + segment->index_duration = avio_rb64(pb); + av_dlog(NULL, "IndexDuration %"PRId64"\n", segment->index_duration); + break; + } + return 0; +} + +static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor) +{ + int code, value, ofs = 0; + char layout[16] = {0}; /* not for printing, may end up not terminated on purpose */ + + do { + code = avio_r8(pb); + value = avio_r8(pb); + av_dlog(NULL, "pixel layout: code %#x\n", code); + + if (ofs <= 14) { + layout[ofs++] = code; + layout[ofs++] = value; + } else + break; /* don't read byte by byte on sneaky files filled with lots of non-zeroes */ + } while (code != 0); /* SMPTE 377M E.2.46 */ + + ff_mxf_decode_pixel_layout(layout, &descriptor->pix_fmt); +} + +static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFDescriptor *descriptor = arg; + descriptor->pix_fmt = AV_PIX_FMT_NONE; + switch(tag) { + case 0x3F01: + descriptor->sub_descriptors_count = avio_rb32(pb); + descriptor->sub_descriptors_refs = av_calloc(descriptor->sub_descriptors_count, sizeof(UID)); + if (!descriptor->sub_descriptors_refs) + return AVERROR(ENOMEM); + avio_skip(pb, 4); /* useless size of objects, always 16 according to specs */ + avio_read(pb, (uint8_t *)descriptor->sub_descriptors_refs, descriptor->sub_descriptors_count * sizeof(UID)); + break; + case 0x3004: + avio_read(pb, descriptor->essence_container_ul, 16); + break; + case 0x3006: + descriptor->linked_track_id = avio_rb32(pb); + break; + case 0x3201: /* PictureEssenceCoding */ + avio_read(pb, descriptor->essence_codec_ul, 16); + break; + case 0x3203: + descriptor->width = avio_rb32(pb); + break; + case 0x3202: + descriptor->height = avio_rb32(pb); + break; + case 0x320C: + descriptor->frame_layout = avio_r8(pb); + break; + case 0x320E: + descriptor->aspect_ratio.num = avio_rb32(pb); + descriptor->aspect_ratio.den = avio_rb32(pb); + break; + case 0x3212: + descriptor->field_dominance = avio_r8(pb); + break; + case 0x3301: + descriptor->component_depth = avio_rb32(pb); + break; + case 0x3302: + descriptor->horiz_subsampling = avio_rb32(pb); + break; + case 0x3308: + descriptor->vert_subsampling = avio_rb32(pb); + break; + case 0x3D03: + descriptor->sample_rate.num = avio_rb32(pb); + descriptor->sample_rate.den = avio_rb32(pb); + break; + case 0x3D06: /* SoundEssenceCompression */ + avio_read(pb, descriptor->essence_codec_ul, 16); + break; + case 0x3D07: + descriptor->channels = avio_rb32(pb); + break; + case 0x3D01: + descriptor->bits_per_sample = avio_rb32(pb); + break; + case 0x3401: + mxf_read_pixel_layout(pb, descriptor); + break; + default: + /* Private uid used by SONY C0023S01.mxf */ + if (IS_KLV_KEY(uid, mxf_sony_mpeg4_extradata)) { + if (descriptor->extradata) + av_log(NULL, AV_LOG_WARNING, "Duplicate sony_mpeg4_extradata\n"); + av_free(descriptor->extradata); + descriptor->extradata_size = 0; + descriptor->extradata = av_malloc(size); + if (!descriptor->extradata) + return AVERROR(ENOMEM); + descriptor->extradata_size = size; + avio_read(pb, descriptor->extradata, size); + } + break; + } + return 0; +} + +/* + * Match an uid independently of the version byte and up to len common bytes + * Returns: boolean + */ +static int mxf_match_uid(const UID key, const UID uid, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i != 7 && key[i] != uid[i]) + return 0; + } + return 1; +} + +static const MXFCodecUL *mxf_get_codec_ul(const MXFCodecUL *uls, UID *uid) +{ + while (uls->uid[0]) { + if(mxf_match_uid(uls->uid, *uid, uls->matching_len)) + break; + uls++; + } + return uls; +} + +static void *mxf_resolve_strong_ref(MXFContext *mxf, UID *strong_ref, enum MXFMetadataSetType type) +{ + int i; + + if (!strong_ref) + return NULL; + for (i = 0; i < mxf->metadata_sets_count; i++) { + if (!memcmp(*strong_ref, mxf->metadata_sets[i]->uid, 16) && + (type == AnyType || mxf->metadata_sets[i]->type == type)) { + return mxf->metadata_sets[i]; + } + } + return NULL; +} + +static const MXFCodecUL mxf_picture_essence_container_uls[] = { + // video essence container uls + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, 14, AV_CODEC_ID_DVVIDEO }, /* DV 625 25mbps */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x05,0x00,0x00 }, 14, AV_CODEC_ID_RAWVIDEO }, /* Uncompressed Picture */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +/* EC ULs for intra-only formats */ +static const MXFCodecUL mxf_intra_only_essence_container_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x00,0x00 }, 14, AV_CODEC_ID_MPEG2VIDEO }, /* MXF-GC SMPTE D-10 Mappings */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +/* intra-only PictureEssenceCoding ULs, where no corresponding EC UL exists */ +static const MXFCodecUL mxf_intra_only_picture_essence_coding_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x01,0x32,0x00,0x00 }, 14, AV_CODEC_ID_H264 }, /* H.264/MPEG-4 AVC Intra Profiles */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +static const MXFCodecUL mxf_sound_essence_container_uls[] = { + // sound essence container uls + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, 14, AV_CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01 }, 14, AV_CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, AV_CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */ + { { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0xFF,0x4B,0x46,0x41,0x41,0x00,0x0D,0x4D,0x4F }, 14, AV_CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, +}; + +static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments) +{ + int i, j, nb_segments = 0; + MXFIndexTableSegment **unsorted_segments; + int last_body_sid = -1, last_index_sid = -1, last_index_start = -1; + + /* count number of segments, allocate arrays and copy unsorted segments */ + for (i = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + nb_segments++; + + if (!nb_segments) + return AVERROR_INVALIDDATA; + + if (!(unsorted_segments = av_calloc(nb_segments, sizeof(*unsorted_segments))) || + !(*sorted_segments = av_calloc(nb_segments, sizeof(**sorted_segments)))) { + av_freep(sorted_segments); + av_free(unsorted_segments); + return AVERROR(ENOMEM); + } + + for (i = j = 0; i < mxf->metadata_sets_count; i++) + if (mxf->metadata_sets[i]->type == IndexTableSegment) + unsorted_segments[j++] = (MXFIndexTableSegment*)mxf->metadata_sets[i]; + + *nb_sorted_segments = 0; + + /* sort segments by {BodySID, IndexSID, IndexStartPosition}, remove duplicates while we're at it */ + for (i = 0; i < nb_segments; i++) { + int best = -1, best_body_sid = -1, best_index_sid = -1, best_index_start = -1; + uint64_t best_index_duration = 0; + + for (j = 0; j < nb_segments; j++) { + MXFIndexTableSegment *s = unsorted_segments[j]; + + /* Require larger BosySID, IndexSID or IndexStartPosition then the previous entry. This removes duplicates. + * We want the smallest values for the keys than what we currently have, unless this is the first such entry this time around. + * If we come across an entry with the same IndexStartPosition but larger IndexDuration, then we'll prefer it over the one we currently have. + */ + if ((i == 0 || s->body_sid > last_body_sid || s->index_sid > last_index_sid || s->index_start_position > last_index_start) && + (best == -1 || s->body_sid < best_body_sid || s->index_sid < best_index_sid || s->index_start_position < best_index_start || + (s->index_start_position == best_index_start && s->index_duration > best_index_duration))) { + best = j; + best_body_sid = s->body_sid; + best_index_sid = s->index_sid; + best_index_start = s->index_start_position; + best_index_duration = s->index_duration; + } + } + + /* no suitable entry found -> we're done */ + if (best == -1) + break; + + (*sorted_segments)[(*nb_sorted_segments)++] = unsorted_segments[best]; + last_body_sid = best_body_sid; + last_index_sid = best_index_sid; + last_index_start = best_index_start; + } + + av_free(unsorted_segments); + + return 0; +} + +/** + * Computes the absolute file offset of the given essence container offset + */ +static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t offset, int64_t *offset_out) +{ + int x; + int64_t offset_in = offset; /* for logging */ + + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (p->body_sid != body_sid) + continue; + + if (offset < p->essence_length || !p->essence_length) { + *offset_out = p->essence_offset + offset; + return 0; + } + + offset -= p->essence_length; + } + + av_log(mxf->fc, AV_LOG_ERROR, + "failed to find absolute offset of %"PRIX64" in BodySID %i - partial file?\n", + offset_in, body_sid); + + return AVERROR_INVALIDDATA; +} + +/** + * Returns the end position of the essence container with given BodySID, or zero if unknown + */ +static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid) +{ + int x; + int64_t ret = 0; + + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (p->body_sid != body_sid) + continue; + + if (!p->essence_length) + return 0; + + ret = p->essence_offset + p->essence_length; + } + + return ret; +} + +/* EditUnit -> absolute offset */ +static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, int64_t *edit_unit_out, int64_t *offset_out, int nag) +{ + int i; + int64_t offset_temp = 0; + + for (i = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; + + edit_unit = FFMAX(edit_unit, s->index_start_position); /* clamp if trying to seek before start */ + + if (edit_unit < s->index_start_position + s->index_duration) { + int64_t index = edit_unit - s->index_start_position; + + if (s->edit_unit_byte_count) + offset_temp += s->edit_unit_byte_count * index; + else if (s->nb_index_entries) { + if (s->nb_index_entries == 2 * s->index_duration + 1) + index *= 2; /* Avid index */ + + if (index < 0 || index >= s->nb_index_entries) { + av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" IndexEntryArray too small\n", + index_table->index_sid, s->index_start_position); + return AVERROR_INVALIDDATA; + } + + offset_temp = s->stream_offset_entries[index]; + } else { + av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n", + index_table->index_sid, s->index_start_position); + return AVERROR_INVALIDDATA; + } + + if (edit_unit_out) + *edit_unit_out = edit_unit; + + return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out); + } else { + /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */ + offset_temp += s->edit_unit_byte_count * s->index_duration; + } + } + + if (nag) + av_log(mxf->fc, AV_LOG_ERROR, "failed to map EditUnit %"PRId64" in IndexSID %i to an offset\n", edit_unit, index_table->index_sid); + + return AVERROR_INVALIDDATA; +} + +static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table) +{ + int i, j, x; + int8_t max_temporal_offset = -128; + + /* first compute how many entries we have */ + for (i = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; + + if (!s->nb_index_entries) { + index_table->nb_ptses = 0; + return 0; /* no TemporalOffsets */ + } + + index_table->nb_ptses += s->index_duration; + } + + /* paranoid check */ + if (index_table->nb_ptses <= 0) + return 0; + + if (!(index_table->ptses = av_calloc(index_table->nb_ptses, sizeof(int64_t))) || + !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry)))) { + av_freep(&index_table->ptses); + return AVERROR(ENOMEM); + } + + /* we may have a few bad TemporalOffsets + * make sure the corresponding PTSes don't have the bogus value 0 */ + for (x = 0; x < index_table->nb_ptses; x++) + index_table->ptses[x] = AV_NOPTS_VALUE; + + /** + * We have this: + * + * x TemporalOffset + * 0: 0 + * 1: 1 + * 2: 1 + * 3: -2 + * 4: 1 + * 5: 1 + * 6: -2 + * + * We want to transform it into this: + * + * x DTS PTS + * 0: -1 0 + * 1: 0 3 + * 2: 1 1 + * 3: 2 2 + * 4: 3 6 + * 5: 4 4 + * 6: 5 5 + * + * We do this by bucket sorting x by x+TemporalOffset[x] into mxf->ptses, + * then settings mxf->first_dts = -max(TemporalOffset[x]). + * The latter makes DTS <= PTS. + */ + for (i = x = 0; i < index_table->nb_segments; i++) { + MXFIndexTableSegment *s = index_table->segments[i]; + int index_delta = 1; + int n = s->nb_index_entries; + + if (s->nb_index_entries == 2 * s->index_duration + 1) { + index_delta = 2; /* Avid index */ + /* ignore the last entry - it's the size of the essence container */ + n--; + } + + for (j = 0; j < n; j += index_delta, x++) { + int offset = s->temporal_offset_entries[j] / index_delta; + int index = x + offset; + + if (x >= index_table->nb_ptses) { + av_log(mxf->fc, AV_LOG_ERROR, + "x >= nb_ptses - IndexEntryCount %i < IndexDuration %"PRId64"?\n", + s->nb_index_entries, s->index_duration); + break; + } + + index_table->fake_index[x].timestamp = x; + index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0; + + if (index < 0 || index >= index_table->nb_ptses) { + av_log(mxf->fc, AV_LOG_ERROR, + "index entry %i + TemporalOffset %i = %i, which is out of bounds\n", + x, offset, index); + continue; + } + + index_table->ptses[index] = x; + max_temporal_offset = FFMAX(max_temporal_offset, offset); + } + } + + index_table->first_dts = -max_temporal_offset; + + return 0; +} + +/** + * Sorts and collects index table segments into index tables. + * Also computes PTSes if possible. + */ +static int mxf_compute_index_tables(MXFContext *mxf) +{ + int i, j, k, ret, nb_sorted_segments; + MXFIndexTableSegment **sorted_segments = NULL; + + if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) || + nb_sorted_segments <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n"); + return 0; + } + + /* sanity check and count unique BodySIDs/IndexSIDs */ + for (i = 0; i < nb_sorted_segments; i++) { + if (i == 0 || sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) + mxf->nb_index_tables++; + else if (sorted_segments[i-1]->body_sid != sorted_segments[i]->body_sid) { + av_log(mxf->fc, AV_LOG_ERROR, "found inconsistent BodySID\n"); + ret = AVERROR_INVALIDDATA; + goto finish_decoding_index; + } + } + + if (!(mxf->index_tables = av_calloc(mxf->nb_index_tables, sizeof(MXFIndexTable)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n"); + ret = AVERROR(ENOMEM); + goto finish_decoding_index; + } + + /* distribute sorted segments to index tables */ + for (i = j = 0; i < nb_sorted_segments; i++) { + if (i != 0 && sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) { + /* next IndexSID */ + j++; + } + + mxf->index_tables[j].nb_segments++; + } + + for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) { + MXFIndexTable *t = &mxf->index_tables[j]; + + if (!(t->segments = av_calloc(t->nb_segments, sizeof(MXFIndexTableSegment*)))) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment pointer array\n"); + ret = AVERROR(ENOMEM); + goto finish_decoding_index; + } + + if (sorted_segments[i]->index_start_position) + av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i starts at EditUnit %"PRId64" - seeking may not work as expected\n", + sorted_segments[i]->index_sid, sorted_segments[i]->index_start_position); + + memcpy(t->segments, &sorted_segments[i], t->nb_segments * sizeof(MXFIndexTableSegment*)); + t->index_sid = sorted_segments[i]->index_sid; + t->body_sid = sorted_segments[i]->body_sid; + + if ((ret = mxf_compute_ptses_fake_index(mxf, t)) < 0) + goto finish_decoding_index; + + /* fix zero IndexDurations */ + for (k = 0; k < t->nb_segments; k++) { + if (t->segments[k]->index_duration) + continue; + + if (t->nb_segments > 1) + av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n", + t->index_sid, k); + + if (mxf->fc->nb_streams <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n"); + break; + } + + /* assume the first stream's duration is reasonable + * leave index_duration = 0 on further segments in case we have any (unlikely) + */ + t->segments[k]->index_duration = mxf->fc->streams[0]->duration; + break; + } + } + + ret = 0; +finish_decoding_index: + av_free(sorted_segments); + return ret; +} + +static int mxf_is_intra_only(MXFDescriptor *descriptor) +{ + return mxf_get_codec_ul(mxf_intra_only_essence_container_uls, + &descriptor->essence_container_ul)->id != AV_CODEC_ID_NONE || + mxf_get_codec_ul(mxf_intra_only_picture_essence_coding_uls, + &descriptor->essence_codec_ul)->id != AV_CODEC_ID_NONE; +} + +static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimecode *tc) +{ + char buf[AV_TIMECODE_STR_SIZE]; + av_dict_set(pm, key, av_timecode_make_string(tc, buf, 0), 0); + + return 0; +} + +static int mxf_parse_structural_metadata(MXFContext *mxf) +{ + MXFPackage *material_package = NULL; + MXFPackage *temp_package = NULL; + int i, j, k, ret; + + av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count); + /* TODO: handle multiple material packages (OP3x) */ + for (i = 0; i < mxf->packages_count; i++) { + material_package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[i], MaterialPackage); + if (material_package) break; + } + if (!material_package) { + av_log(mxf->fc, AV_LOG_ERROR, "no material package found\n"); + return AVERROR_INVALIDDATA; + } + + for (i = 0; i < material_package->tracks_count; i++) { + MXFPackage *source_package = NULL; + MXFTrack *material_track = NULL; + MXFTrack *source_track = NULL; + MXFTrack *temp_track = NULL; + MXFDescriptor *descriptor = NULL; + MXFStructuralComponent *component = NULL; + MXFTimecodeComponent *mxf_tc = NULL; + UID *essence_container_ul = NULL; + const MXFCodecUL *codec_ul = NULL; + const MXFCodecUL *container_ul = NULL; + const MXFCodecUL *pix_fmt_ul = NULL; + AVStream *st; + AVTimecode tc; + int flags; + + if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n"); + continue; + } + + if ((component = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, TimecodeComponent))) { + mxf_tc = (MXFTimecodeComponent*)component; + flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0; + if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) { + mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc); + } + } + + if (!(material_track->sequence = mxf_resolve_strong_ref(mxf, &material_track->sequence_ref, Sequence))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track sequence strong ref\n"); + continue; + } + + for (j = 0; j < material_track->sequence->structural_components_count; j++) { + component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], TimecodeComponent); + if (!component) + continue; + + mxf_tc = (MXFTimecodeComponent*)component; + flags = mxf_tc->drop_frame == 1 ? AV_TIMECODE_FLAG_DROPFRAME : 0; + if (av_timecode_init(&tc, mxf_tc->rate, flags, mxf_tc->start_frame, mxf->fc) == 0) { + mxf_add_timecode_metadata(&mxf->fc->metadata, "timecode", &tc); + break; + } + } + + /* TODO: handle multiple source clips */ + for (j = 0; j < material_track->sequence->structural_components_count; j++) { + component = mxf_resolve_strong_ref(mxf, &material_track->sequence->structural_components_refs[j], SourceClip); + if (!component) + continue; + + for (k = 0; k < mxf->packages_count; k++) { + temp_package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[k], SourcePackage); + if (!temp_package) + continue; + if (!memcmp(temp_package->package_uid, component->source_package_uid, 16)) { + source_package = temp_package; + break; + } + } + if (!source_package) { + av_dlog(mxf->fc, "material track %d: no corresponding source package found\n", material_track->track_id); + break; + } + for (k = 0; k < source_package->tracks_count; k++) { + if (!(temp_track = mxf_resolve_strong_ref(mxf, &source_package->tracks_refs[k], Track))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track strong ref\n"); + ret = AVERROR_INVALIDDATA; + goto fail_and_free; + } + if (temp_track->track_id == component->source_track_id) { + source_track = temp_track; + break; + } + } + if (!source_track) { + av_log(mxf->fc, AV_LOG_ERROR, "material track %d: no corresponding source track found\n", material_track->track_id); + break; + } + } + if (!source_track || !component) + continue; + + if (!(source_track->sequence = mxf_resolve_strong_ref(mxf, &source_track->sequence_ref, Sequence))) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve source track sequence strong ref\n"); + ret = AVERROR_INVALIDDATA; + goto fail_and_free; + } + + /* 0001GL00.MXF.A1.mxf_opatom.mxf has the same SourcePackageID as 0001GL.MXF.V1.mxf_opatom.mxf + * This would result in both files appearing to have two streams. Work around this by sanity checking DataDefinition */ + if (memcmp(material_track->sequence->data_definition_ul, source_track->sequence->data_definition_ul, 16)) { + av_log(mxf->fc, AV_LOG_ERROR, "material track %d: DataDefinition mismatch\n", material_track->track_id); + continue; + } + + st = avformat_new_stream(mxf->fc, NULL); + if (!st) { + av_log(mxf->fc, AV_LOG_ERROR, "could not allocate stream\n"); + ret = AVERROR(ENOMEM); + goto fail_and_free; + } + st->id = source_track->track_id; + st->priv_data = source_track; + source_track->original_duration = st->duration = component->duration; + if (st->duration == -1) + st->duration = AV_NOPTS_VALUE; + st->start_time = component->start_position; + if (material_track->edit_rate.num <= 0 || material_track->edit_rate.den <= 0) { + av_log(mxf->fc, AV_LOG_WARNING, + "invalid edit rate (%d/%d) found on stream #%d, defaulting to 25/1\n", + material_track->edit_rate.num, material_track->edit_rate.den, st->index); + material_track->edit_rate = (AVRational){25, 1}; + } + avpriv_set_pts_info(st, 64, material_track->edit_rate.den, material_track->edit_rate.num); + + /* ensure SourceTrack EditRate == MaterialTrack EditRate since only the former is accessible via st->priv_data */ + source_track->edit_rate = material_track->edit_rate; + + PRINT_KEY(mxf->fc, "data definition ul", source_track->sequence->data_definition_ul); + codec_ul = mxf_get_codec_ul(ff_mxf_data_definition_uls, &source_track->sequence->data_definition_ul); + st->codec->codec_type = codec_ul->id; + + source_package->descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor_ref, AnyType); + if (source_package->descriptor) { + if (source_package->descriptor->type == MultipleDescriptor) { + for (j = 0; j < source_package->descriptor->sub_descriptors_count; j++) { + MXFDescriptor *sub_descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor->sub_descriptors_refs[j], Descriptor); + + if (!sub_descriptor) { + av_log(mxf->fc, AV_LOG_ERROR, "could not resolve sub descriptor strong ref\n"); + continue; + } + if (sub_descriptor->linked_track_id == source_track->track_id) { + descriptor = sub_descriptor; + break; + } + } + } else if (source_package->descriptor->type == Descriptor) + descriptor = source_package->descriptor; + } + if (!descriptor) { + av_log(mxf->fc, AV_LOG_INFO, "source track %d: stream %d, no descriptor found\n", source_track->track_id, st->index); + continue; + } + PRINT_KEY(mxf->fc, "essence codec ul", descriptor->essence_codec_ul); + PRINT_KEY(mxf->fc, "essence container ul", descriptor->essence_container_ul); + essence_container_ul = &descriptor->essence_container_ul; + /* HACK: replacing the original key with mxf_encrypted_essence_container + * is not allowed according to s429-6, try to find correct information anyway */ + if (IS_KLV_KEY(essence_container_ul, mxf_encrypted_essence_container)) { + av_log(mxf->fc, AV_LOG_INFO, "broken encrypted mxf file\n"); + for (k = 0; k < mxf->metadata_sets_count; k++) { + MXFMetadataSet *metadata = mxf->metadata_sets[k]; + if (metadata->type == CryptoContext) { + essence_container_ul = &((MXFCryptoContext *)metadata)->source_container_ul; + break; + } + } + } + + /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */ + codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul); + st->codec->codec_id = (enum AVCodecID)codec_ul->id; + av_log(mxf->fc, AV_LOG_VERBOSE, "%s: Universal Label: ", + avcodec_get_name(st->codec->codec_id)); + for (k = 0; k < 16; k++) { + av_log(mxf->fc, AV_LOG_VERBOSE, "%.2x", + descriptor->essence_codec_ul[k]); + if (!(k+1 & 19) || k == 5) + av_log(mxf->fc, AV_LOG_VERBOSE, "."); + } + av_log(mxf->fc, AV_LOG_VERBOSE, "\n"); + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + source_track->intra_only = mxf_is_intra_only(descriptor); + container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul); + if (st->codec->codec_id == AV_CODEC_ID_NONE) + st->codec->codec_id = container_ul->id; + st->codec->width = descriptor->width; + st->codec->height = descriptor->height; /* Field height, not frame height */ + switch (descriptor->frame_layout) { + case SegmentedFrame: + /* This one is a weird layout I don't fully understand. */ + av_log(mxf->fc, AV_LOG_INFO, "SegmentedFrame layout isn't currently supported\n"); + break; + case FullFrame: + st->codec->field_order = AV_FIELD_PROGRESSIVE; + break; + case OneField: + /* Every other line is stored and needs to be duplicated. */ + av_log(mxf->fc, AV_LOG_INFO, "OneField frame layout isn't currently supported\n"); + break; /* The correct thing to do here is fall through, but by breaking we might be + able to decode some streams at half the vertical resolution, rather than not al all. + It's also for compatibility with the old behavior. */ + case MixedFields: + break; + case SeparateFields: + st->codec->height *= 2; /* Turn field height into frame height. */ + break; + default: + av_log(mxf->fc, AV_LOG_INFO, "Unknown frame layout type: %d\n", descriptor->frame_layout); + } + if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO) { + st->codec->pix_fmt = descriptor->pix_fmt; + if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { + pix_fmt_ul = mxf_get_codec_ul(ff_mxf_pixel_format_uls, + &descriptor->essence_codec_ul); + st->codec->pix_fmt = (enum AVPixelFormat)pix_fmt_ul->id; + if (st->codec->pix_fmt == AV_PIX_FMT_NONE) { + /* support files created before RP224v10 by defaulting to UYVY422 + if subsampling is 4:2:2 and component depth is 8-bit */ + if (descriptor->horiz_subsampling == 2 && + descriptor->vert_subsampling == 1 && + descriptor->component_depth == 8) { + st->codec->pix_fmt = AV_PIX_FMT_UYVY422; + } + } + } + } + st->need_parsing = AVSTREAM_PARSE_HEADERS; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul); + /* Only overwrite existing codec ID if it is unset or A-law, which is the default according to SMPTE RP 224. */ + if (st->codec->codec_id == AV_CODEC_ID_NONE || (st->codec->codec_id == AV_CODEC_ID_PCM_ALAW && (enum AVCodecID)container_ul->id != AV_CODEC_ID_NONE)) + st->codec->codec_id = (enum AVCodecID)container_ul->id; + st->codec->channels = descriptor->channels; + st->codec->bits_per_coded_sample = descriptor->bits_per_sample; + + if (descriptor->sample_rate.den > 0) { + avpriv_set_pts_info(st, 64, descriptor->sample_rate.den, descriptor->sample_rate.num); + st->codec->sample_rate = descriptor->sample_rate.num / descriptor->sample_rate.den; + } else { + av_log(mxf->fc, AV_LOG_WARNING, "invalid sample rate (%d/%d) found for stream #%d, time base forced to 1/48000\n", + descriptor->sample_rate.num, descriptor->sample_rate.den, st->index); + avpriv_set_pts_info(st, 64, 1, 48000); + } + + /* if duration is set, rescale it from EditRate to SampleRate */ + if (st->duration != AV_NOPTS_VALUE) + st->duration = av_rescale_q(st->duration, av_inv_q(material_track->edit_rate), st->time_base); + + /* TODO: implement AV_CODEC_ID_RAWAUDIO */ + if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { + if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24) + st->codec->codec_id = AV_CODEC_ID_PCM_S24LE; + else if (descriptor->bits_per_sample == 32) + st->codec->codec_id = AV_CODEC_ID_PCM_S32LE; + } else if (st->codec->codec_id == AV_CODEC_ID_PCM_S16BE) { + if (descriptor->bits_per_sample > 16 && descriptor->bits_per_sample <= 24) + st->codec->codec_id = AV_CODEC_ID_PCM_S24BE; + else if (descriptor->bits_per_sample == 32) + st->codec->codec_id = AV_CODEC_ID_PCM_S32BE; + } else if (st->codec->codec_id == AV_CODEC_ID_MP2) { + st->need_parsing = AVSTREAM_PARSE_FULL; + } + } + if (descriptor->extradata) { + st->codec->extradata = av_mallocz(descriptor->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) + memcpy(st->codec->extradata, descriptor->extradata, descriptor->extradata_size); + } else if(st->codec->codec_id == AV_CODEC_ID_H264) { + ff_generate_avci_extradata(st); + } + if (st->codec->codec_type != AVMEDIA_TYPE_DATA && (*essence_container_ul)[15] > 0x01) { + /* TODO: decode timestamps */ + st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS; + } + } + + ret = 0; +fail_and_free: + return ret; +} + +static int mxf_read_utf16_string(AVIOContext *pb, int size, char** str) +{ + int ret; + size_t buf_size; + + if (size < 0) + return AVERROR(EINVAL); + + buf_size = size + size/2 + 1; + *str = av_malloc(buf_size); + if (!*str) + return AVERROR(ENOMEM); + + if ((ret = avio_get_str16be(pb, size, *str, buf_size)) < 0) { + av_freep(str); + return ret; + } + + return ret; +} + +static int mxf_uid_to_str(UID uid, char **str) +{ + int i; + char *p; + p = *str = av_mallocz(sizeof(UID) * 2 + 4 + 1); + if (!p) + return AVERROR(ENOMEM); + for (i = 0; i < sizeof(UID); i++) { + snprintf(p, 2 + 1, "%.2x", uid[i]); + p += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) { + snprintf(p, 1 + 1, "-"); + p++; + } + } + return 0; +} + +static int mxf_timestamp_to_str(uint64_t timestamp, char **str) +{ + struct tm time = {0}; + time.tm_year = (timestamp >> 48) - 1900; + time.tm_mon = (timestamp >> 40 & 0xFF) - 1; + time.tm_mday = (timestamp >> 32 & 0xFF); + time.tm_hour = (timestamp >> 24 & 0xFF); + time.tm_min = (timestamp >> 16 & 0xFF); + time.tm_sec = (timestamp >> 8 & 0xFF); + + /* ensure month/day are valid */ + time.tm_mon = FFMAX(time.tm_mon, 0); + time.tm_mday = FFMAX(time.tm_mday, 1); + + *str = av_mallocz(32); + if (!*str) + return AVERROR(ENOMEM); + strftime(*str, 32, "%Y-%m-%d %H:%M:%S", &time); + + return 0; +} + +#define SET_STR_METADATA(pb, name, str) do { \ + if ((ret = mxf_read_utf16_string(pb, size, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +#define SET_UID_METADATA(pb, name, var, str) do { \ + avio_read(pb, var, 16); \ + if ((ret = mxf_uid_to_str(var, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +#define SET_TS_METADATA(pb, name, var, str) do { \ + var = avio_rb64(pb); \ + if ((ret = mxf_timestamp_to_str(var, &str)) < 0) \ + return ret; \ + av_dict_set(&s->metadata, name, str, AV_DICT_DONT_STRDUP_VAL); \ +} while (0) + +static int mxf_read_identification_metadata(void *arg, AVIOContext *pb, int tag, int size, UID _uid, int64_t klv_offset) +{ + MXFContext *mxf = arg; + AVFormatContext *s = mxf->fc; + int ret; + UID uid = { 0 }; + char *str = NULL; + uint64_t ts; + switch (tag) { + case 0x3C01: + SET_STR_METADATA(pb, "company_name", str); + break; + case 0x3C02: + SET_STR_METADATA(pb, "product_name", str); + break; + case 0x3C04: + SET_STR_METADATA(pb, "product_version", str); + break; + case 0x3C05: + SET_UID_METADATA(pb, "product_uid", uid, str); + break; + case 0x3C06: + SET_TS_METADATA(pb, "modification_date", ts, str); + break; + case 0x3C08: + SET_STR_METADATA(pb, "application_platform", str); + break; + case 0x3C09: + SET_UID_METADATA(pb, "generation_uid", uid, str); + break; + case 0x3C0A: + SET_UID_METADATA(pb, "uid", uid, str); + break; + } + return 0; +} + +static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }, mxf_read_primer_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x03,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x01,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x03,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x02,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x30,0x00 }, mxf_read_identification_metadata }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_source_package, sizeof(MXFPackage), SourcePackage }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_material_package, sizeof(MXFPackage), MaterialPackage }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0F,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x11,0x00 }, mxf_read_source_clip, sizeof(MXFStructuralComponent), SourceClip }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x44,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), MultipleDescriptor }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Generic Sound */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x28,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* CDCI */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x29,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* RGBA */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x51,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG 2 Video */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* Wave */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* AES3 */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */ + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext }, + { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment }, + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType }, +}; + +static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadFunc *read_child, int ctx_size, enum MXFMetadataSetType type) +{ + AVIOContext *pb = mxf->fc->pb; + MXFMetadataSet *ctx = ctx_size ? av_mallocz(ctx_size) : mxf; + uint64_t klv_end = avio_tell(pb) + klv->length; + + if (!ctx) + return AVERROR(ENOMEM); + while (avio_tell(pb) + 4 < klv_end && !url_feof(pb)) { + int ret; + int tag = avio_rb16(pb); + int size = avio_rb16(pb); /* KLV specified by 0x53 */ + uint64_t next = avio_tell(pb) + size; + UID uid = {0}; + + av_dlog(mxf->fc, "local tag %#04x size %d\n", tag, size); + if (!size) { /* ignore empty tag, needed for some files with empty UMID tag */ + av_log(mxf->fc, AV_LOG_ERROR, "local tag %#04x with 0 size\n", tag); + continue; + } + if (tag > 0x7FFF) { /* dynamic tag */ + int i; + for (i = 0; i < mxf->local_tags_count; i++) { + int local_tag = AV_RB16(mxf->local_tags+i*18); + if (local_tag == tag) { + memcpy(uid, mxf->local_tags+i*18+2, 16); + av_dlog(mxf->fc, "local tag %#04x\n", local_tag); + PRINT_KEY(mxf->fc, "uid", uid); + } + } + } + if (ctx_size && tag == 0x3C0A) + avio_read(pb, ctx->uid, 16); + else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0) + return ret; + + /* Accept the 64k local set limit being exceeded (Avid). Don't accept + * it extending past the end of the KLV though (zzuf5.mxf). */ + if (avio_tell(pb) > klv_end) { + if (ctx_size) + av_free(ctx); + + av_log(mxf->fc, AV_LOG_ERROR, + "local tag %#04x extends past end of local set @ %#"PRIx64"\n", + tag, klv->offset); + return AVERROR_INVALIDDATA; + } else if (avio_tell(pb) <= next) /* only seek forward, else this can loop for a long time */ + avio_seek(pb, next, SEEK_SET); + } + if (ctx_size) ctx->type = type; + return ctx_size ? mxf_add_metadata_set(mxf, ctx) : 0; +} + +/** + * Seeks to the previous partition, if possible + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_seek_to_previous_partition(MXFContext *mxf) +{ + AVIOContext *pb = mxf->fc->pb; + + if (!mxf->current_partition || + mxf->run_in + mxf->current_partition->previous_partition <= mxf->last_forward_tell) + return 0; /* we've parsed all partitions */ + + /* seek to previous partition */ + avio_seek(pb, mxf->run_in + mxf->current_partition->previous_partition, SEEK_SET); + mxf->current_partition = NULL; + + av_dlog(mxf->fc, "seeking to previous partition\n"); + + return 1; +} + +/** + * Called when essence is encountered + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_parse_handle_essence(MXFContext *mxf) +{ + AVIOContext *pb = mxf->fc->pb; + int64_t ret; + + if (mxf->parsing_backward) { + return mxf_seek_to_previous_partition(mxf); + } else { + if (!mxf->footer_partition) { + av_dlog(mxf->fc, "no footer\n"); + return 0; + } + + av_dlog(mxf->fc, "seeking to footer\n"); + + /* remember where we were so we don't end up seeking further back than this */ + mxf->last_forward_tell = avio_tell(pb); + + if (!pb->seekable) { + av_log(mxf->fc, AV_LOG_INFO, "file is not seekable - not parsing footer\n"); + return -1; + } + + /* seek to footer partition and parse backward */ + if ((ret = avio_seek(pb, mxf->run_in + mxf->footer_partition, SEEK_SET)) < 0) { + av_log(mxf->fc, AV_LOG_ERROR, "failed to seek to footer @ 0x%"PRIx64" (%"PRId64") - partial file?\n", + mxf->run_in + mxf->footer_partition, ret); + return ret; + } + + mxf->current_partition = NULL; + mxf->parsing_backward = 1; + } + + return 1; +} + +/** + * Called when the next partition or EOF is encountered + * @return <= 0 if we should stop parsing, > 0 if we should keep going + */ +static int mxf_parse_handle_partition_or_eof(MXFContext *mxf) +{ + return mxf->parsing_backward ? mxf_seek_to_previous_partition(mxf) : 1; +} + +/** + * Figures out the proper offset and length of the essence container in each partition + */ +static void mxf_compute_essence_containers(MXFContext *mxf) +{ + int x; + + /* everything is already correct */ + if (mxf->op == OPAtom) + return; + + for (x = 0; x < mxf->partitions_count; x++) { + MXFPartition *p = &mxf->partitions[x]; + + if (!p->body_sid) + continue; /* BodySID == 0 -> no essence */ + + if (x >= mxf->partitions_count - 1) + break; /* last partition - can't compute length (and we don't need to) */ + + /* essence container spans to the next partition */ + p->essence_length = mxf->partitions[x+1].this_partition - p->essence_offset; + + if (p->essence_length < 0) { + /* next ThisPartition < essence_offset */ + p->essence_length = 0; + av_log(mxf->fc, AV_LOG_ERROR, + "partition %i: bad ThisPartition = %"PRIX64"\n", + x+1, mxf->partitions[x+1].this_partition); + } + } +} + +static int64_t round_to_kag(int64_t position, int kag_size) +{ + /* TODO: account for run-in? the spec isn't clear whether KAG should account for it */ + /* NOTE: kag_size may be any integer between 1 - 2^10 */ + int64_t ret = (position / kag_size) * kag_size; + return ret == position ? ret : ret + kag_size; +} + +static int is_pcm(enum AVCodecID codec_id) +{ + /* we only care about "normal" PCM codecs until we get samples */ + return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD; +} + +/** + * Deal with the case where for some audio atoms EditUnitByteCount is + * very small (2, 4..). In those cases we should read more than one + * sample per call to mxf_read_packet(). + */ +static void mxf_handle_small_eubc(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + + /* assuming non-OPAtom == frame wrapped + * no sane writer would wrap 2 byte PCM packets with 20 byte headers.. */ + if (mxf->op != OPAtom) + return; + + /* expect PCM with exactly one index table segment and a small (< 32) EUBC */ + if (s->nb_streams != 1 || + s->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO || + !is_pcm(s->streams[0]->codec->codec_id) || + mxf->nb_index_tables != 1 || + mxf->index_tables[0].nb_segments != 1 || + mxf->index_tables[0].segments[0]->edit_unit_byte_count >= 32) + return; + + /* arbitrarily default to 48 kHz PAL audio frame size */ + /* TODO: We could compute this from the ratio between the audio + * and video edit rates for 48 kHz NTSC we could use the + * 1802-1802-1802-1802-1801 pattern. */ + mxf->edit_units_per_packet = 1920; +} + +static int mxf_read_header(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + KLVPacket klv; + int64_t essence_offset = 0; + int ret; + + mxf->last_forward_tell = INT64_MAX; + mxf->edit_units_per_packet = 1; + + if (!mxf_read_sync(s->pb, mxf_header_partition_pack_key, 14)) { + av_log(s, AV_LOG_ERROR, "could not find header partition pack key\n"); + return AVERROR_INVALIDDATA; + } + avio_seek(s->pb, -14, SEEK_CUR); + mxf->fc = s; + mxf->run_in = avio_tell(s->pb); + + while (!url_feof(s->pb)) { + const MXFMetadataReadTableEntry *metadata; + + if (klv_read_packet(&klv, s->pb) < 0) { + /* EOF - seek to previous partition or stop */ + if(mxf_parse_handle_partition_or_eof(mxf) <= 0) + break; + else + continue; + } + + PRINT_KEY(s, "read header", klv.key); + av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); + if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) || + IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_avid_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_system_item_key)) { + + if (!mxf->current_partition) { + av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n"); + return AVERROR_INVALIDDATA; + } + + if (!mxf->current_partition->essence_offset) { + /* for OP1a we compute essence_offset + * for OPAtom we point essence_offset after the KL (usually op1a_essence_offset + 20 or 25) + * TODO: for OP1a we could eliminate this entire if statement, always stopping parsing at op1a_essence_offset + * for OPAtom we still need the actual essence_offset though (the KL's length can vary) + */ + int64_t op1a_essence_offset = + round_to_kag(mxf->current_partition->this_partition + + mxf->current_partition->pack_length, mxf->current_partition->kag_size) + + round_to_kag(mxf->current_partition->header_byte_count, mxf->current_partition->kag_size) + + round_to_kag(mxf->current_partition->index_byte_count, mxf->current_partition->kag_size); + + if (mxf->op == OPAtom) { + /* point essence_offset to the actual data + * OPAtom has all the essence in one big KLV + */ + mxf->current_partition->essence_offset = avio_tell(s->pb); + mxf->current_partition->essence_length = klv.length; + } else { + /* NOTE: op1a_essence_offset may be less than to klv.offset (C0023S01.mxf) */ + mxf->current_partition->essence_offset = op1a_essence_offset; + } + } + + if (!essence_offset) + essence_offset = klv.offset; + + /* seek to footer, previous partition or stop */ + if (mxf_parse_handle_essence(mxf) <= 0) + break; + continue; + } else if (!memcmp(klv.key, mxf_header_partition_pack_key, 13) && + klv.key[13] >= 2 && klv.key[13] <= 4 && mxf->current_partition) { + /* next partition pack - keep going, seek to previous partition or stop */ + if(mxf_parse_handle_partition_or_eof(mxf) <= 0) + break; + else if (mxf->parsing_backward) + continue; + /* we're still parsing forward. proceed to parsing this partition pack */ + } + + for (metadata = mxf_metadata_read_table; metadata->read; metadata++) { + if (IS_KLV_KEY(klv.key, metadata->key)) { + int res; + if (klv.key[5] == 0x53) { + res = mxf_read_local_tags(mxf, &klv, metadata->read, metadata->ctx_size, metadata->type); + } else { + uint64_t next = avio_tell(s->pb) + klv.length; + res = metadata->read(mxf, s->pb, 0, klv.length, klv.key, klv.offset); + + /* only seek forward, else this can loop for a long time */ + if (avio_tell(s->pb) > next) { + av_log(s, AV_LOG_ERROR, "read past end of KLV @ %#"PRIx64"\n", + klv.offset); + return AVERROR_INVALIDDATA; + } + + avio_seek(s->pb, next, SEEK_SET); + } + if (res < 0) { + av_log(s, AV_LOG_ERROR, "error reading header metadata\n"); + return res; + } + break; + } + } + if (!metadata->read) + avio_skip(s->pb, klv.length); + } + /* FIXME avoid seek */ + if (!essence_offset) { + av_log(s, AV_LOG_ERROR, "no essence\n"); + return AVERROR_INVALIDDATA; + } + avio_seek(s->pb, essence_offset, SEEK_SET); + + mxf_compute_essence_containers(mxf); + + /* we need to do this before computing the index tables + * to be able to fill in zero IndexDurations with st->duration */ + if ((ret = mxf_parse_structural_metadata(mxf)) < 0) + goto fail; + + if ((ret = mxf_compute_index_tables(mxf)) < 0) + goto fail; + + if (mxf->nb_index_tables > 1) { + /* TODO: look up which IndexSID to use via EssenceContainerData */ + av_log(mxf->fc, AV_LOG_INFO, "got %i index tables - only the first one (IndexSID %i) will be used\n", + mxf->nb_index_tables, mxf->index_tables[0].index_sid); + } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) { + av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + mxf_handle_small_eubc(s); + + return 0; +fail: + mxf_read_close(s); + + return ret; +} + +/** + * Sets mxf->current_edit_unit based on what offset we're currently at. + * @return next_ofs if OK, <0 on error + */ +static int64_t mxf_set_current_edit_unit(MXFContext *mxf, int64_t current_offset) +{ + int64_t last_ofs = -1, next_ofs = -1; + MXFIndexTable *t = &mxf->index_tables[0]; + + /* this is called from the OP1a demuxing logic, which means there + * may be no index tables */ + if (mxf->nb_index_tables <= 0) + return -1; + + /* find mxf->current_edit_unit so that the next edit unit starts ahead of current_offset */ + while (mxf->current_edit_unit >= 0) { + if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0) + return -1; + + if (next_ofs <= last_ofs) { + /* large next_ofs didn't change or current_edit_unit wrapped + * around this fixes the infinite loop on zzuf3.mxf */ + av_log(mxf->fc, AV_LOG_ERROR, + "next_ofs didn't change. not deriving packet timestamps\n"); + return -1; + } + + if (next_ofs > current_offset) + break; + + last_ofs = next_ofs; + mxf->current_edit_unit++; + } + + /* not checking mxf->current_edit_unit >= t->nb_ptses here since CBR files may lack IndexEntryArrays */ + if (mxf->current_edit_unit < 0) + return -1; + + return next_ofs; +} + +static int mxf_compute_sample_count(MXFContext *mxf, int stream_index, uint64_t *sample_count) +{ + int i, total = 0, size = 0; + AVStream *st = mxf->fc->streams[stream_index]; + MXFTrack *track = st->priv_data; + AVRational time_base = av_inv_q(track->edit_rate); + AVRational sample_rate = av_inv_q(st->time_base); + const MXFSamplesPerFrame *spf = NULL; + + if ((sample_rate.num / sample_rate.den) == 48000) + spf = ff_mxf_get_samples_per_frame(mxf->fc, time_base); + if (!spf) { + int remainder = (sample_rate.num * time_base.num) % (time_base.den * sample_rate.den); + *sample_count = av_q2d(av_mul_q((AVRational){mxf->current_edit_unit, 1}, + av_mul_q(sample_rate, time_base))); + if (remainder) + av_log(mxf->fc, AV_LOG_WARNING, + "seeking detected on stream #%d with time base (%d/%d) and sample rate (%d/%d), audio pts won't be accurate.\n", + stream_index, time_base.num, time_base.den, sample_rate.num, sample_rate.den); + return 0; + } + + while (spf->samples_per_frame[size]) { + total += spf->samples_per_frame[size]; + size++; + } + + av_assert2(size); + + *sample_count = (mxf->current_edit_unit / size) * (uint64_t)total; + for (i = 0; i < mxf->current_edit_unit % size; i++) { + *sample_count += spf->samples_per_frame[i]; + } + + return 0; +} + +static int mxf_set_audio_pts(MXFContext *mxf, AVCodecContext *codec, AVPacket *pkt) +{ + MXFTrack *track = mxf->fc->streams[pkt->stream_index]->priv_data; + pkt->pts = track->sample_count; + if (codec->channels <= 0 || av_get_bits_per_sample(codec->codec_id) <= 0) + return AVERROR(EINVAL); + track->sample_count += pkt->size / (codec->channels * (int64_t)av_get_bits_per_sample(codec->codec_id) / 8); + return 0; +} + +static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt) +{ + KLVPacket klv; + MXFContext *mxf = s->priv_data; + + while (!url_feof(s->pb)) { + int ret; + if (klv_read_packet(&klv, s->pb) < 0) + return -1; + PRINT_KEY(s, "read packet", klv.key); + av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset); + if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) { + ret = mxf_decrypt_triplet(s, pkt, &klv); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n"); + return AVERROR_INVALIDDATA; + } + return 0; + } + if (IS_KLV_KEY(klv.key, mxf_essence_element_key) || + IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) { + int index = mxf_get_stream_index(s, &klv); + int64_t next_ofs, next_klv; + AVStream *st; + MXFTrack *track; + AVCodecContext *codec; + + if (index < 0) { + av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12)); + goto skip; + } + + st = s->streams[index]; + track = st->priv_data; + + if (s->streams[index]->discard == AVDISCARD_ALL) + goto skip; + + next_klv = avio_tell(s->pb) + klv.length; + next_ofs = mxf_set_current_edit_unit(mxf, klv.offset); + + if (next_ofs >= 0 && next_klv > next_ofs) { + /* if this check is hit then it's possible OPAtom was treated as OP1a + * truncate the packet since it's probably very large (>2 GiB is common) */ + avpriv_request_sample(s, + "OPAtom misinterpreted as OP1a?" + "KLV for edit unit %i extending into " + "next edit unit", + mxf->current_edit_unit); + klv.length = next_ofs - avio_tell(s->pb); + } + + /* check for 8 channels AES3 element */ + if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) { + if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) { + av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n"); + return AVERROR_INVALIDDATA; + } + } else { + ret = av_get_packet(s->pb, pkt, klv.length); + if (ret < 0) + return ret; + } + pkt->stream_index = index; + pkt->pos = klv.offset; + + codec = s->streams[index]->codec; + if (codec->codec_type == AVMEDIA_TYPE_VIDEO && next_ofs >= 0) { + /* mxf->current_edit_unit good - see if we have an index table to derive timestamps from */ + MXFIndexTable *t = &mxf->index_tables[0]; + + if (mxf->nb_index_tables >= 1 && mxf->current_edit_unit < t->nb_ptses) { + pkt->dts = mxf->current_edit_unit + t->first_dts; + pkt->pts = t->ptses[mxf->current_edit_unit]; + } else if (track->intra_only) { + /* intra-only -> PTS = EditUnit. + * let utils.c figure out DTS since it can be < PTS if low_delay = 0 (Sony IMX30) */ + pkt->pts = mxf->current_edit_unit; + } + } else if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + int ret = mxf_set_audio_pts(mxf, codec, pkt); + if (ret < 0) + return ret; + } + + /* seek for truncated packets */ + avio_seek(s->pb, next_klv, SEEK_SET); + + return 0; + } else + skip: + avio_skip(s->pb, klv.length); + } + return AVERROR_EOF; +} + +static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + int ret, size; + int64_t ret64, pos, next_pos; + AVStream *st; + MXFIndexTable *t; + int edit_units; + + if (mxf->op != OPAtom) + return mxf_read_packet_old(s, pkt); + + /* OPAtom - clip wrapped demuxing */ + /* NOTE: mxf_read_header() makes sure nb_index_tables > 0 for OPAtom */ + st = s->streams[0]; + t = &mxf->index_tables[0]; + + if (mxf->current_edit_unit >= st->duration) + return AVERROR_EOF; + + edit_units = FFMIN(mxf->edit_units_per_packet, st->duration - mxf->current_edit_unit); + + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit, NULL, &pos, 1)) < 0) + return ret; + + /* compute size by finding the next edit unit or the end of the essence container + * not pretty, but it works */ + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + edit_units, NULL, &next_pos, 0)) < 0 && + (next_pos = mxf_essence_container_end(mxf, t->body_sid)) <= 0) { + av_log(s, AV_LOG_ERROR, "unable to compute the size of the last packet\n"); + return AVERROR_INVALIDDATA; + } + + if ((size = next_pos - pos) <= 0) { + av_log(s, AV_LOG_ERROR, "bad size: %i\n", size); + return AVERROR_INVALIDDATA; + } + + if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0) + return ret64; + + if ((size = av_get_packet(s->pb, pkt, size)) < 0) + return size; + + pkt->stream_index = 0; + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses && + mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) { + pkt->dts = mxf->current_edit_unit + t->first_dts; + pkt->pts = t->ptses[mxf->current_edit_unit]; + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + int ret = mxf_set_audio_pts(mxf, st->codec, pkt); + if (ret < 0) + return ret; + } + + mxf->current_edit_unit += edit_units; + + return 0; +} + +static int mxf_read_close(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + MXFIndexTableSegment *seg; + int i; + + av_freep(&mxf->packages_refs); + + for (i = 0; i < s->nb_streams; i++) + s->streams[i]->priv_data = NULL; + + for (i = 0; i < mxf->metadata_sets_count; i++) { + switch (mxf->metadata_sets[i]->type) { + case Descriptor: + av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->extradata); + break; + case MultipleDescriptor: + av_freep(&((MXFDescriptor *)mxf->metadata_sets[i])->sub_descriptors_refs); + break; + case Sequence: + av_freep(&((MXFSequence *)mxf->metadata_sets[i])->structural_components_refs); + break; + case SourcePackage: + case MaterialPackage: + av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs); + break; + case IndexTableSegment: + seg = (MXFIndexTableSegment *)mxf->metadata_sets[i]; + av_freep(&seg->temporal_offset_entries); + av_freep(&seg->flag_entries); + av_freep(&seg->stream_offset_entries); + break; + default: + break; + } + av_freep(&mxf->metadata_sets[i]); + } + av_freep(&mxf->partitions); + av_freep(&mxf->metadata_sets); + av_freep(&mxf->aesc); + av_freep(&mxf->local_tags); + + if (mxf->index_tables) { + for (i = 0; i < mxf->nb_index_tables; i++) { + av_freep(&mxf->index_tables[i].segments); + av_freep(&mxf->index_tables[i].ptses); + av_freep(&mxf->index_tables[i].fake_index); + } + } + av_freep(&mxf->index_tables); + + return 0; +} + +static int mxf_probe(AVProbeData *p) { + const uint8_t *bufp = p->buf; + const uint8_t *end = p->buf + p->buf_size; + + if (p->buf_size < sizeof(mxf_header_partition_pack_key)) + return 0; + + /* Must skip Run-In Sequence and search for MXF header partition pack key SMPTE 377M 5.5 */ + end -= sizeof(mxf_header_partition_pack_key); + for (; bufp < end; bufp++) { + if (IS_KLV_KEY(bufp, mxf_header_partition_pack_key)) + return AVPROBE_SCORE_MAX; + } + return 0; +} + +/* rudimentary byte seek */ +/* XXX: use MXF Index */ +static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags) +{ + AVStream *st = s->streams[stream_index]; + int64_t seconds; + MXFContext* mxf = s->priv_data; + int64_t seekpos; + int i, ret; + int64_t ret64; + MXFIndexTable *t; + MXFTrack *source_track = st->priv_data; + + /* if audio then truncate sample_time to EditRate */ + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + sample_time = av_rescale_q(sample_time, st->time_base, av_inv_q(source_track->edit_rate)); + + if (mxf->nb_index_tables <= 0) { + if (!s->bit_rate) + return AVERROR_INVALIDDATA; + if (sample_time < 0) + sample_time = 0; + seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den); + + if ((ret64 = avio_seek(s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET)) < 0) + return ret64; + ff_update_cur_dts(s, st, sample_time); + mxf->current_edit_unit = sample_time; + } else { + t = &mxf->index_tables[0]; + + /* clamp above zero, else ff_index_search_timestamp() returns negative + * this also means we allow seeking before the start */ + sample_time = FFMAX(sample_time, 0); + + if (t->fake_index) { + /* behave as if we have a proper index */ + if ((sample_time = ff_index_search_timestamp(t->fake_index, t->nb_ptses, sample_time, flags)) < 0) + return sample_time; + } else { + /* no IndexEntryArray (one or more CBR segments) + * make sure we don't seek past the end */ + sample_time = FFMIN(sample_time, source_track->original_duration - 1); + } + + if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0) + return ret; + + ff_update_cur_dts(s, st, sample_time); + mxf->current_edit_unit = sample_time; + avio_seek(s->pb, seekpos, SEEK_SET); + } + + // Update all tracks sample count + for (i = 0; i < s->nb_streams; i++) { + AVStream *cur_st = s->streams[i]; + MXFTrack *cur_track = cur_st->priv_data; + uint64_t current_sample_count = 0; + if (cur_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + ret = mxf_compute_sample_count(mxf, i, ¤t_sample_count); + if (ret < 0) + return ret; + + cur_track->sample_count = current_sample_count; + } + } + return 0; +} + +AVInputFormat ff_mxf_demuxer = { + .name = "mxf", + .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), + .priv_data_size = sizeof(MXFContext), + .read_probe = mxf_probe, + .read_header = mxf_read_header, + .read_packet = mxf_read_packet, + .read_close = mxf_read_close, + .read_seek = mxf_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/mxfdec.o libavformat/mxfdec.o: libavformat/mxfdec.c \ + libavutil/aes.h libavutil/attributes.h libavutil/version.h \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/mathematics.h libavcodec/bytestream.h libavutil/common.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/timecode.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/mxf.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfdec.o Binary file ffmpeg/libavformat/mxfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxfenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,2173 @@ +/* + * MXF muxer + * Copyright (c) 2008 GUCAS, Zhentan Feng + * Copyright (c) 2008 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * References + * SMPTE 336M KLV Data Encoding Protocol Using Key-Length-Value + * SMPTE 377M MXF File Format Specifications + * SMPTE 379M MXF Generic Container + * SMPTE 381M Mapping MPEG Streams into the MXF Generic Container + * SMPTE RP210: SMPTE Metadata Dictionary + * SMPTE RP224: Registry of SMPTE Universal Labels + */ + +//#define DEBUG + +#include +#include + +#include "libavutil/opt.h" +#include "libavutil/random_seed.h" +#include "libavutil/timecode.h" +#include "libavutil/avassert.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/dnxhddata.h" +#include "audiointerleave.h" +#include "avformat.h" +#include "internal.h" +#include "mxf.h" +#include "config.h" + +extern AVOutputFormat ff_mxf_d10_muxer; + +#define EDIT_UNITS_PER_BODY 250 +#define KAG_SIZE 512 + +typedef struct { + int local_tag; + UID uid; +} MXFLocalTagPair; + +typedef struct { + uint8_t flags; + uint64_t offset; + unsigned slice_offset; ///< offset of audio slice + uint16_t temporal_ref; +} MXFIndexEntry; + +typedef struct { + AudioInterleaveContext aic; + UID track_essence_element_key; + int index; ///< index in mxf_essence_container_uls table + const UID *codec_ul; + int order; ///< interleaving order if dts are equal + int interlaced; ///< whether picture is interlaced + int field_dominance; ///< tff=1, bff=2 + int component_depth; + int temporal_reordering; + AVRational aspect_ratio; ///< display aspect ratio + int closed_gop; ///< gop is closed, used in mpeg-2 frame parsing +} MXFStreamContext; + +typedef struct { + UID container_ul; + UID element_ul; + UID codec_ul; + void (*write_desc)(AVFormatContext *, AVStream *); +} MXFContainerEssenceEntry; + +static const struct { + enum AVCodecID id; + int index; +} mxf_essence_mappings[] = { + { AV_CODEC_ID_MPEG2VIDEO, 0 }, + { AV_CODEC_ID_PCM_S24LE, 1 }, + { AV_CODEC_ID_PCM_S16LE, 1 }, + { AV_CODEC_ID_DVVIDEO, 15 }, + { AV_CODEC_ID_DNXHD, 24 }, + { AV_CODEC_ID_NONE } +}; + +static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st); + +static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 }, + mxf_write_mpegvideo_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x03,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x03,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_aes3_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_wav_desc }, + // D-10 625/50 PAL 50mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x01 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // D-10 525/60 NTSC 50mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x02,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x02 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x02,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // D-10 625/50 PAL 40mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x03,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x03 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x03,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // D-10 525/60 NTSC 40mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x04 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // D-10 625/50 PAL 30mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x05,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x05 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x05,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // D-10 525/60 NTSC 30mb/s + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x06 }, + mxf_write_cdci_desc }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, + mxf_write_generic_sound_desc }, + // DV Unknown + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x7F,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x00,0x00,0x00 }, + mxf_write_cdci_desc }, + // DV25 525/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x40,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 }, + mxf_write_cdci_desc }, + // DV25 625/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x02,0x00 }, + mxf_write_cdci_desc }, + // DV50 525/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x50,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x03,0x00 }, + mxf_write_cdci_desc }, + // DV50 625/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x51,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x04,0x00 }, + mxf_write_cdci_desc }, + // DV100 1080/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x60,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x05,0x00 }, + mxf_write_cdci_desc }, + // DV100 1080/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x61,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x06,0x00 }, + mxf_write_cdci_desc }, + // DV100 720/60 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x62,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x07,0x00 }, + mxf_write_cdci_desc }, + // DV100 720/50 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x63,0x01 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x08,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 10bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x01,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 8bit medium + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x03,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080p 8bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x04,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 10bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x07,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 8bit medium + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x08,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 1080i 8bit high + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x09,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 10bit + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x10,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit high + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x11,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit medium + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x12,0x00,0x00 }, + mxf_write_cdci_desc }, + // DNxHD 720p 8bit low + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0A,0x04,0x01,0x02,0x02,0x71,0x13,0x00,0x00 }, + mxf_write_cdci_desc }, + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + NULL }, +}; + +typedef struct MXFContext { + AVClass *av_class; + int64_t footer_partition_offset; + int essence_container_count; + AVRational time_base; + int header_written; + MXFIndexEntry *index_entries; + unsigned edit_units_count; + uint64_t timestamp; ///< timestamp, as year(16),month(8),day(8),hour(8),minutes(8),msec/4(8) + uint8_t slice_count; ///< index slice count minus 1 (1 if no audio, 0 otherwise) + int last_indexed_edit_unit; + uint64_t *body_partition_offset; + unsigned body_partitions_count; + int last_key_index; ///< index of last key frame + uint64_t duration; + AVTimecode tc; ///< timecode context + AVStream *timecode_track; + int timecode_base; ///< rounded time code base (25 or 30) + int edit_unit_byte_count; ///< fixed edit unit byte count + uint64_t body_offset; + uint32_t instance_number; + uint8_t umid[16]; ///< unique material identifier +} MXFContext; + +static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd }; +static const uint8_t umid_ul[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x01,0x0D,0x00,0x13 }; + +/** + * complete key for operation pattern, partitions, and primer pack + */ +static const uint8_t op1a_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x09,0x00 }; +static const uint8_t footer_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }; // ClosedComplete +static const uint8_t primer_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x05,0x01,0x00 }; +static const uint8_t index_table_segment_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }; +static const uint8_t random_index_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x11,0x01,0x00 }; +static const uint8_t header_open_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x01,0x00 }; // OpenIncomplete +static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x02,0x04,0x00 }; // ClosedComplete +static const uint8_t klv_fill_key[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 }; +static const uint8_t body_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }; // ClosedComplete + +/** + * partial key for header metadata + */ +static const uint8_t header_metadata_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01 }; +static const uint8_t multiple_desc_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x0D,0x01,0x03,0x01,0x02,0x7F,0x01,0x00 }; + +/** + * SMPTE RP210 http://www.smpte-ra.org/mdd/index.html + */ +static const MXFLocalTagPair mxf_local_tag_batch[] = { + // preface set + { 0x3C0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x02,0x00,0x00,0x00,0x00}}, /* Instance UID */ + { 0x3B02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x04,0x00,0x00}}, /* Last Modified Date */ + { 0x3B05, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x01,0x05,0x00,0x00,0x00}}, /* Version */ + { 0x3B06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x04,0x00,0x00}}, /* Identifications reference */ + { 0x3B03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x01,0x00,0x00}}, /* Content Storage reference */ + { 0x3B09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x03,0x00,0x00,0x00,0x00}}, /* Operational Pattern UL */ + { 0x3B0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x10,0x02,0x01,0x00,0x00}}, /* Essence Containers UL batch */ + { 0x3B0B, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x10,0x02,0x02,0x00,0x00}}, /* DM Schemes UL batch */ + // Identification + { 0x3C09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x01,0x00,0x00,0x00}}, /* This Generation UID */ + { 0x3C01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x02,0x01,0x00,0x00}}, /* Company Name */ + { 0x3C02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x03,0x01,0x00,0x00}}, /* Product Name */ + { 0x3C04, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x05,0x01,0x00,0x00}}, /* Version String */ + { 0x3C05, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x07,0x00,0x00,0x00}}, /* Product ID */ + { 0x3C06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x03,0x00,0x00}}, /* Modification Date */ + // Content Storage + { 0x1901, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x05,0x01,0x00,0x00}}, /* Package strong reference batch */ + { 0x1902, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x05,0x02,0x00,0x00}}, /* Package strong reference batch */ + // Essence Container Data + { 0x2701, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x06,0x01,0x00,0x00,0x00}}, /* Linked Package UID */ + { 0x3F07, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x01,0x03,0x04,0x04,0x00,0x00,0x00,0x00}}, /* BodySID */ + // Package + { 0x4401, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x10,0x00,0x00,0x00,0x00}}, /* Package UID */ + { 0x4405, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x01,0x03,0x00,0x00}}, /* Package Creation Date */ + { 0x4404, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x05,0x00,0x00}}, /* Package Modified Date */ + { 0x4403, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x05,0x00,0x00}}, /* Tracks Strong reference array */ + { 0x4701, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x03,0x00,0x00}}, /* Descriptor */ + // Track + { 0x4801, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x01,0x07,0x01,0x01,0x00,0x00,0x00,0x00}}, /* Track ID */ + { 0x4804, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x01,0x04,0x01,0x03,0x00,0x00,0x00,0x00}}, /* Track Number */ + { 0x4B01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x30,0x04,0x05,0x00,0x00,0x00,0x00}}, /* Edit Rate */ + { 0x4B02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x03,0x00,0x00}}, /* Origin */ + { 0x4803, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x04,0x00,0x00}}, /* Sequence reference */ + // Sequence + { 0x0201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x07,0x01,0x00,0x00,0x00,0x00,0x00}}, /* Data Definition UL */ + { 0x0202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x02,0x01,0x01,0x03,0x00,0x00}}, /* Duration */ + { 0x1001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x09,0x00,0x00}}, /* Structural Components reference array */ + // Source Clip + { 0x1201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x04,0x00,0x00}}, /* Start position */ + { 0x1101, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x01,0x00,0x00,0x00}}, /* SourcePackageID */ + { 0x1102, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x02,0x00,0x00,0x00}}, /* SourceTrackID */ + // Timecode Component + { 0x1501, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x05,0x00,0x00}}, /* Start Time Code */ + { 0x1502, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x04,0x01,0x01,0x02,0x06,0x00,0x00}}, /* Rounded Time Code Base */ + { 0x1503, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x04,0x01,0x01,0x05,0x00,0x00,0x00}}, /* Drop Frame */ + // File Descriptor + { 0x3F01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x04,0x06,0x0B,0x00,0x00}}, /* Sub Descriptors reference array */ + { 0x3006, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x06,0x01,0x01,0x03,0x05,0x00,0x00,0x00}}, /* Linked Track ID */ + { 0x3001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x06,0x01,0x01,0x00,0x00,0x00,0x00}}, /* SampleRate */ + { 0x3004, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x01,0x02,0x00,0x00}}, /* Essence Container */ + // Generic Picture Essence Descriptor + { 0x320C, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Frame Layout */ + { 0x320D, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x03,0x02,0x05,0x00,0x00,0x00}}, /* Video Line Map */ + { 0x3203, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x02,0x02,0x00,0x00,0x00}}, /* Stored Width */ + { 0x3202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x02,0x01,0x00,0x00,0x00}}, /* Stored Height */ + { 0x3209, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0C,0x00,0x00,0x00}}, /* Display Width */ + { 0x3208, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x0B,0x00,0x00,0x00}}, /* Display Height */ + { 0x320E, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x01,0x01,0x01,0x00,0x00,0x00}}, /* Aspect Ratio */ + { 0x3201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x06,0x01,0x00,0x00,0x00,0x00}}, /* Picture Essence Coding */ + { 0x3212, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x03,0x01,0x06,0x00,0x00,0x00}}, /* Field Dominance (Opt) */ + // CDCI Picture Essence Descriptor + { 0x3301, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x05,0x03,0x0A,0x00,0x00,0x00}}, /* Component Depth */ + { 0x3302, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x01,0x05,0x00,0x00,0x00}}, /* Horizontal Subsampling */ + // Generic Sound Essence Descriptor + { 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Locked/Unlocked */ + { 0x3D03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x01,0x01,0x00,0x00}}, /* Audio sampling rate */ + { 0x3D07, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x01,0x01,0x04,0x00,0x00,0x00}}, /* ChannelCount */ + { 0x3D01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x03,0x04,0x00,0x00,0x00}}, /* Quantization bits */ + { 0x3D06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x02,0x04,0x02,0x00,0x00,0x00,0x00}}, /* Sound Essence Compression */ + // Index Table Segment + { 0x3F0B, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x05,0x30,0x04,0x06,0x00,0x00,0x00,0x00}}, /* Index Edit Rate */ + { 0x3F0C, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x01,0x03,0x01,0x0A,0x00,0x00}}, /* Index Start Position */ + { 0x3F0D, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x02,0x01,0x01,0x02,0x00,0x00}}, /* Index Duration */ + { 0x3F05, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x06,0x02,0x01,0x00,0x00,0x00,0x00}}, /* Edit Unit Byte Count */ + { 0x3F06, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x01,0x03,0x04,0x05,0x00,0x00,0x00,0x00}}, /* IndexSID */ + { 0x3F08, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x04,0x04,0x01,0x01,0x00,0x00,0x00}}, /* Slice Count */ + { 0x3F09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x04,0x04,0x01,0x06,0x00,0x00,0x00}}, /* Delta Entry Array */ + { 0x3F0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x04,0x04,0x02,0x05,0x00,0x00,0x00}}, /* Index Entry Array */ + // MPEG video Descriptor + { 0x8000, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x06,0x02,0x01,0x0B,0x00,0x00}}, /* BitRate */ + { 0x8007, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x06,0x02,0x01,0x0A,0x00,0x00}}, /* ProfileAndLevel */ + // Wave Audio Essence Descriptor + { 0x3D09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x03,0x05,0x00,0x00,0x00}}, /* Average Bytes Per Second */ + { 0x3D0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Block Align */ +}; + +static void mxf_write_uuid(AVIOContext *pb, enum MXFMetadataSetType type, int value) +{ + avio_write(pb, uuid_base, 12); + avio_wb16(pb, type); + avio_wb16(pb, value); +} + +static void mxf_write_umid(AVFormatContext *s, int type) +{ + MXFContext *mxf = s->priv_data; + avio_write(s->pb, umid_ul, 13); + avio_wb24(s->pb, mxf->instance_number); + avio_write(s->pb, mxf->umid, 15); + avio_w8(s->pb, type); +} + +static void mxf_write_refs_count(AVIOContext *pb, int ref_count) +{ + avio_wb32(pb, ref_count); + avio_wb32(pb, 16); +} + +static int klv_ber_length(uint64_t len) +{ + if (len < 128) + return 1; + else + return (av_log2(len) >> 3) + 2; +} + +static int klv_encode_ber_length(AVIOContext *pb, uint64_t len) +{ + // Determine the best BER size + int size; + if (len < 128) { + //short form + avio_w8(pb, len); + return 1; + } + + size = (av_log2(len) >> 3) + 1; + + // long form + avio_w8(pb, 0x80 + size); + while(size) { + size--; + avio_w8(pb, len >> 8 * size & 0xff); + } + return 0; +} + +static void klv_encode_ber4_length(AVIOContext *pb, int len) +{ + avio_w8(pb, 0x80 + 3); + avio_wb24(pb, len); +} + +/* + * Get essence container ul index + */ +static int mxf_get_essence_container_ul_index(enum AVCodecID id) +{ + int i; + for (i = 0; mxf_essence_mappings[i].id; i++) + if (mxf_essence_mappings[i].id == id) + return mxf_essence_mappings[i].index; + return -1; +} + +static void mxf_write_primer_pack(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int local_tag_number, i = 0; + + local_tag_number = FF_ARRAY_ELEMS(mxf_local_tag_batch); + + avio_write(pb, primer_pack_key, 16); + klv_encode_ber_length(pb, local_tag_number * 18 + 8); + + avio_wb32(pb, local_tag_number); // local_tag num + avio_wb32(pb, 18); // item size, always 18 according to the specs + + for (i = 0; i < local_tag_number; i++) { + avio_wb16(pb, mxf_local_tag_batch[i].local_tag); + avio_write(pb, mxf_local_tag_batch[i].uid, 16); + } +} + +static void mxf_write_local_tag(AVIOContext *pb, int size, int tag) +{ + avio_wb16(pb, tag); + avio_wb16(pb, size); +} + +static void mxf_write_metadata_key(AVIOContext *pb, unsigned int value) +{ + avio_write(pb, header_metadata_key, 13); + avio_wb24(pb, value); +} + +static void mxf_free(AVFormatContext *s) +{ + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + av_freep(&st->priv_data); + } +} + +static const MXFCodecUL *mxf_get_data_definition_ul(int type) +{ + const MXFCodecUL *uls = ff_mxf_data_definition_uls; + while (uls->uid[0]) { + if (type == uls->id) + break; + uls++; + } + return uls; +} + +//one EC -> one descriptor. N ECs -> MultipleDescriptor + N descriptors +#define DESCRIPTOR_COUNT(essence_container_count) \ + (essence_container_count > 1 ? essence_container_count + 1 : essence_container_count) + +static void mxf_write_essence_container_refs(AVFormatContext *s) +{ + MXFContext *c = s->priv_data; + AVIOContext *pb = s->pb; + int i; + + mxf_write_refs_count(pb, DESCRIPTOR_COUNT(c->essence_container_count)); + av_log(s,AV_LOG_DEBUG, "essence container count:%d\n", c->essence_container_count); + for (i = 0; i < c->essence_container_count; i++) { + MXFStreamContext *sc = s->streams[i]->priv_data; + avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); + } + + if (c->essence_container_count > 1) + avio_write(pb, multiple_desc_ul, 16); +} + +static void mxf_write_preface(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + + mxf_write_metadata_key(pb, 0x012f00); + PRINT_KEY(s, "preface key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 130 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count)); + + // write preface set uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, Preface, 0); + PRINT_KEY(s, "preface uid", pb->buf_ptr - 16); + + // last modified date + mxf_write_local_tag(pb, 8, 0x3B02); + avio_wb64(pb, mxf->timestamp); + + // write version + mxf_write_local_tag(pb, 2, 0x3B05); + avio_wb16(pb, 258); // v1.2 + + // write identification_refs + mxf_write_local_tag(pb, 16 + 8, 0x3B06); + mxf_write_refs_count(pb, 1); + mxf_write_uuid(pb, Identification, 0); + + // write content_storage_refs + mxf_write_local_tag(pb, 16, 0x3B03); + mxf_write_uuid(pb, ContentStorage, 0); + + // operational pattern + mxf_write_local_tag(pb, 16, 0x3B09); + avio_write(pb, op1a_ul, 16); + + // write essence_container_refs + mxf_write_local_tag(pb, 8 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count), 0x3B0A); + mxf_write_essence_container_refs(s); + + // write dm_scheme_refs + mxf_write_local_tag(pb, 8, 0x3B0B); + avio_wb64(pb, 0); +} + +/* + * Write a local tag containing an ascii string as utf-16 + */ +static void mxf_write_local_tag_utf16(AVIOContext *pb, int tag, const char *value) +{ + int i, size = strlen(value); + mxf_write_local_tag(pb, size*2, tag); + for (i = 0; i < size; i++) + avio_wb16(pb, value[i]); +} + +static void mxf_write_identification(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + const char *company = "FFmpeg"; + const char *product = "OP1a Muxer"; + const char *version; + int length; + + mxf_write_metadata_key(pb, 0x013000); + PRINT_KEY(s, "identification key", pb->buf_ptr - 16); + + version = s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT ? + "0.0.0" : AV_STRINGIFY(LIBAVFORMAT_VERSION); + length = 84 + (strlen(company)+strlen(product)+strlen(version))*2; // utf-16 + klv_encode_ber_length(pb, length); + + // write uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, Identification, 0); + PRINT_KEY(s, "identification uid", pb->buf_ptr - 16); + + // write generation uid + mxf_write_local_tag(pb, 16, 0x3C09); + mxf_write_uuid(pb, Identification, 1); + + mxf_write_local_tag_utf16(pb, 0x3C01, company); // Company Name + mxf_write_local_tag_utf16(pb, 0x3C02, product); // Product Name + mxf_write_local_tag_utf16(pb, 0x3C04, version); // Version String + + // write product uid + mxf_write_local_tag(pb, 16, 0x3C05); + mxf_write_uuid(pb, Identification, 2); + + // modification date + mxf_write_local_tag(pb, 8, 0x3C06); + avio_wb64(pb, mxf->timestamp); +} + +static void mxf_write_content_storage(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + + mxf_write_metadata_key(pb, 0x011800); + PRINT_KEY(s, "content storage key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 92); + + // write uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, ContentStorage, 0); + PRINT_KEY(s, "content storage uid", pb->buf_ptr - 16); + + // write package reference + mxf_write_local_tag(pb, 16 * 2 + 8, 0x1901); + mxf_write_refs_count(pb, 2); + mxf_write_uuid(pb, MaterialPackage, 0); + mxf_write_uuid(pb, SourcePackage, 0); + + // write essence container data + mxf_write_local_tag(pb, 8 + 16, 0x1902); + mxf_write_refs_count(pb, 1); + mxf_write_uuid(pb, EssenceContainerData, 0); +} + +static void mxf_write_track(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + MXFStreamContext *sc = st->priv_data; + + mxf_write_metadata_key(pb, 0x013b00); + PRINT_KEY(s, "track key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 80); + + // write track uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, type == MaterialPackage ? Track : Track + TypeBottom, st->index); + PRINT_KEY(s, "track uid", pb->buf_ptr - 16); + + // write track id + mxf_write_local_tag(pb, 4, 0x4801); + avio_wb32(pb, st->index+2); + + // write track number + mxf_write_local_tag(pb, 4, 0x4804); + if (type == MaterialPackage) + avio_wb32(pb, 0); // track number of material package is 0 + else + avio_write(pb, sc->track_essence_element_key + 12, 4); + + mxf_write_local_tag(pb, 8, 0x4B01); + avio_wb32(pb, mxf->time_base.den); + avio_wb32(pb, mxf->time_base.num); + + // write origin + mxf_write_local_tag(pb, 8, 0x4B02); + avio_wb64(pb, 0); + + // write sequence refs + mxf_write_local_tag(pb, 16, 0x4803); + mxf_write_uuid(pb, type == MaterialPackage ? Sequence: Sequence + TypeBottom, st->index); +} + +static const uint8_t smpte_12m_timecode_track_data_ul[] = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x01,0x01,0x00,0x00,0x00 }; + +static void mxf_write_common_fields(AVFormatContext *s, AVStream *st) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + + // find data define uls + mxf_write_local_tag(pb, 16, 0x0201); + if (st == mxf->timecode_track) + avio_write(pb, smpte_12m_timecode_track_data_ul, 16); + else { + const MXFCodecUL *data_def_ul = mxf_get_data_definition_ul(st->codec->codec_type); + avio_write(pb, data_def_ul->uid, 16); + } + + // write duration + mxf_write_local_tag(pb, 8, 0x0202); + avio_wb64(pb, mxf->duration); +} + +static void mxf_write_sequence(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + enum MXFMetadataSetType component; + + mxf_write_metadata_key(pb, 0x010f00); + PRINT_KEY(s, "sequence key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 80); + + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, type == MaterialPackage ? Sequence: Sequence + TypeBottom, st->index); + + PRINT_KEY(s, "sequence uid", pb->buf_ptr - 16); + mxf_write_common_fields(s, st); + + // write structural component + mxf_write_local_tag(pb, 16 + 8, 0x1001); + mxf_write_refs_count(pb, 1); + if (st == mxf->timecode_track) + component = TimecodeComponent; + else + component = SourceClip; + if (type == SourcePackage) + component += TypeBottom; + mxf_write_uuid(pb, component, st->index); +} + +static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + + mxf_write_metadata_key(pb, 0x011400); + klv_encode_ber_length(pb, 75); + + // UID + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, type == MaterialPackage ? TimecodeComponent : + TimecodeComponent + TypeBottom, st->index); + + mxf_write_common_fields(s, st); + + // Start Time Code + mxf_write_local_tag(pb, 8, 0x1501); + avio_wb64(pb, mxf->tc.start); + + // Rounded Time Code Base + mxf_write_local_tag(pb, 2, 0x1502); + avio_wb16(pb, mxf->timecode_base); + + // Drop Frame + mxf_write_local_tag(pb, 1, 0x1503); + avio_w8(pb, !!(mxf->tc.flags & AV_TIMECODE_FLAG_DROPFRAME)); +} + +static void mxf_write_structural_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) +{ + AVIOContext *pb = s->pb; + int i; + + mxf_write_metadata_key(pb, 0x011100); + PRINT_KEY(s, "sturctural component key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 108); + + // write uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, type == MaterialPackage ? SourceClip: SourceClip + TypeBottom, st->index); + + PRINT_KEY(s, "structural component uid", pb->buf_ptr - 16); + mxf_write_common_fields(s, st); + + // write start_position + mxf_write_local_tag(pb, 8, 0x1201); + avio_wb64(pb, 0); + + // write source package uid, end of the reference + mxf_write_local_tag(pb, 32, 0x1101); + if (type == SourcePackage) { + for (i = 0; i < 4; i++) + avio_wb64(pb, 0); + } else + mxf_write_umid(s, 1); + + // write source track id + mxf_write_local_tag(pb, 4, 0x1102); + if (type == SourcePackage) + avio_wb32(pb, 0); + else + avio_wb32(pb, st->index+2); +} + +static void mxf_write_multi_descriptor(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + const uint8_t *ul; + int i; + + mxf_write_metadata_key(pb, 0x014400); + PRINT_KEY(s, "multiple descriptor key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 64 + 16LL * s->nb_streams); + + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, MultipleDescriptor, 0); + PRINT_KEY(s, "multi_desc uid", pb->buf_ptr - 16); + + // write sample rate + mxf_write_local_tag(pb, 8, 0x3001); + avio_wb32(pb, mxf->time_base.den); + avio_wb32(pb, mxf->time_base.num); + + // write essence container ul + mxf_write_local_tag(pb, 16, 0x3004); + if (mxf->essence_container_count > 1) + ul = multiple_desc_ul; + else { + MXFStreamContext *sc = s->streams[0]->priv_data; + ul = mxf_essence_container_uls[sc->index].container_ul; + } + avio_write(pb, ul, 16); + + // write sub descriptor refs + mxf_write_local_tag(pb, s->nb_streams * 16 + 8, 0x3F01); + mxf_write_refs_count(pb, s->nb_streams); + for (i = 0; i < s->nb_streams; i++) + mxf_write_uuid(pb, SubDescriptor, i); +} + +static void mxf_write_generic_desc(AVFormatContext *s, AVStream *st, const UID key, unsigned size) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + AVIOContext *pb = s->pb; + + avio_write(pb, key, 16); + klv_encode_ber4_length(pb, size+20+8+12+20); + + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, SubDescriptor, st->index); + + mxf_write_local_tag(pb, 4, 0x3006); + avio_wb32(pb, st->index+2); + + mxf_write_local_tag(pb, 8, 0x3001); + avio_wb32(pb, mxf->time_base.den); + avio_wb32(pb, mxf->time_base.num); + + mxf_write_local_tag(pb, 16, 0x3004); + avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); +} + +static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x51,0x00 }; +static const UID mxf_wav_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }; +static const UID mxf_aes3_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }; +static const UID mxf_cdci_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x28,0x00 }; +static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }; + +static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID key, unsigned size) +{ + MXFStreamContext *sc = st->priv_data; + AVIOContext *pb = s->pb; + int stored_height = (st->codec->height+15)/16*16; + int display_height; + int f1, f2; + unsigned desc_size = size+8+8+8+8+8+8+5+16+sc->interlaced*4+12+20; + if (sc->interlaced && sc->field_dominance) + desc_size += 5; + + mxf_write_generic_desc(s, st, key, desc_size); + + mxf_write_local_tag(pb, 4, 0x3203); + avio_wb32(pb, st->codec->width); + + mxf_write_local_tag(pb, 4, 0x3202); + avio_wb32(pb, stored_height>>sc->interlaced); + + mxf_write_local_tag(pb, 4, 0x3209); + avio_wb32(pb, st->codec->width); + + if (st->codec->height == 608) // PAL + VBI + display_height = 576; + else if (st->codec->height == 512) // NTSC + VBI + display_height = 486; + else + display_height = st->codec->height; + + mxf_write_local_tag(pb, 4, 0x3208); + avio_wb32(pb, display_height>>sc->interlaced); + + // component depth + mxf_write_local_tag(pb, 4, 0x3301); + avio_wb32(pb, sc->component_depth); + + // horizontal subsampling + mxf_write_local_tag(pb, 4, 0x3302); + avio_wb32(pb, 2); + + // frame layout + mxf_write_local_tag(pb, 1, 0x320C); + avio_w8(pb, sc->interlaced); + + // video line map + switch (st->codec->height) { + case 576: f1 = 23; f2 = st->codec->codec_id == AV_CODEC_ID_DVVIDEO ? 335 : 336; break; + case 608: f1 = 7; f2 = 320; break; + case 480: f1 = 20; f2 = st->codec->codec_id == AV_CODEC_ID_DVVIDEO ? 285 : 283; break; + case 512: f1 = 7; f2 = 270; break; + case 720: f1 = 26; f2 = 0; break; // progressive + case 1080: f1 = 21; f2 = 584; break; + default: f1 = 0; f2 = 0; break; + } + + if (!sc->interlaced) { + f2 = 0; + f1 *= 2; + } + + mxf_write_local_tag(pb, 12+sc->interlaced*4, 0x320D); + avio_wb32(pb, sc->interlaced ? 2 : 1); + avio_wb32(pb, 4); + avio_wb32(pb, f1); + if (sc->interlaced) + avio_wb32(pb, f2); + + mxf_write_local_tag(pb, 8, 0x320E); + avio_wb32(pb, sc->aspect_ratio.num); + avio_wb32(pb, sc->aspect_ratio.den); + + mxf_write_local_tag(pb, 16, 0x3201); + avio_write(pb, *sc->codec_ul, 16); + + if (sc->interlaced && sc->field_dominance) { + mxf_write_local_tag(pb, 1, 0x3212); + avio_w8(pb, sc->field_dominance); + } + +} + +static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key, 0); +} + +static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st) +{ + AVIOContext *pb = s->pb; + int profile_and_level = (st->codec->profile<<4) | st->codec->level; + + mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key, 8+5); + + // bit rate + mxf_write_local_tag(pb, 4, 0x8000); + avio_wb32(pb, st->codec->bit_rate); + + // profile and level + mxf_write_local_tag(pb, 1, 0x8007); + if (!st->codec->profile) + profile_and_level |= 0x80; // escape bit + avio_w8(pb, profile_and_level); +} + +static void mxf_write_generic_sound_common(AVFormatContext *s, AVStream *st, const UID key, unsigned size) +{ + AVIOContext *pb = s->pb; + + mxf_write_generic_desc(s, st, key, size+5+12+8+8); + + // audio locked + mxf_write_local_tag(pb, 1, 0x3D02); + avio_w8(pb, 1); + + // write audio sampling rate + mxf_write_local_tag(pb, 8, 0x3D03); + avio_wb32(pb, st->codec->sample_rate); + avio_wb32(pb, 1); + + mxf_write_local_tag(pb, 4, 0x3D07); + avio_wb32(pb, st->codec->channels); + + mxf_write_local_tag(pb, 4, 0x3D01); + avio_wb32(pb, av_get_bits_per_sample(st->codec->codec_id)); +} + +static void mxf_write_wav_common(AVFormatContext *s, AVStream *st, const UID key, unsigned size) +{ + AVIOContext *pb = s->pb; + + mxf_write_generic_sound_common(s, st, key, size+6+8); + + mxf_write_local_tag(pb, 2, 0x3D0A); + avio_wb16(pb, st->codec->block_align); + + // avg bytes per sec + mxf_write_local_tag(pb, 4, 0x3D09); + avio_wb32(pb, st->codec->block_align*st->codec->sample_rate); +} + +static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_wav_common(s, st, mxf_wav_descriptor_key, 0); +} + +static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_wav_common(s, st, mxf_aes3_descriptor_key, 0); +} + +static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st) +{ + mxf_write_generic_sound_common(s, st, mxf_generic_sound_descriptor_key, 0); +} + +static void mxf_write_package(AVFormatContext *s, enum MXFMetadataSetType type) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int i, track_count = s->nb_streams+1; + + if (type == MaterialPackage) { + mxf_write_metadata_key(pb, 0x013600); + PRINT_KEY(s, "Material Package key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 92 + 16*track_count); + } else { + mxf_write_metadata_key(pb, 0x013700); + PRINT_KEY(s, "Source Package key", pb->buf_ptr - 16); + klv_encode_ber_length(pb, 112 + 16*track_count); // 20 bytes length for descriptor reference + } + + // write uid + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, type, 0); + av_log(s,AV_LOG_DEBUG, "package type:%d\n", type); + PRINT_KEY(s, "package uid", pb->buf_ptr - 16); + + // write package umid + mxf_write_local_tag(pb, 32, 0x4401); + mxf_write_umid(s, type == SourcePackage); + PRINT_KEY(s, "package umid second part", pb->buf_ptr - 16); + + // package creation date + mxf_write_local_tag(pb, 8, 0x4405); + avio_wb64(pb, mxf->timestamp); + + // package modified date + mxf_write_local_tag(pb, 8, 0x4404); + avio_wb64(pb, mxf->timestamp); + + // write track refs + mxf_write_local_tag(pb, track_count*16 + 8, 0x4403); + mxf_write_refs_count(pb, track_count); + mxf_write_uuid(pb, type == MaterialPackage ? Track : + Track + TypeBottom, -1); // timecode track + for (i = 0; i < s->nb_streams; i++) + mxf_write_uuid(pb, type == MaterialPackage ? Track : Track + TypeBottom, i); + + // write multiple descriptor reference + if (type == SourcePackage) { + mxf_write_local_tag(pb, 16, 0x4701); + if (s->nb_streams > 1) { + mxf_write_uuid(pb, MultipleDescriptor, 0); + mxf_write_multi_descriptor(s); + } else + mxf_write_uuid(pb, SubDescriptor, 0); + } + + // write timecode track + mxf_write_track(s, mxf->timecode_track, type); + mxf_write_sequence(s, mxf->timecode_track, type); + mxf_write_timecode_component(s, mxf->timecode_track, type); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + mxf_write_track(s, st, type); + mxf_write_sequence(s, st, type); + mxf_write_structural_component(s, st, type); + + if (type == SourcePackage) { + MXFStreamContext *sc = st->priv_data; + mxf_essence_container_uls[sc->index].write_desc(s, st); + } + } +} + +static int mxf_write_essence_container_data(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + + mxf_write_metadata_key(pb, 0x012300); + klv_encode_ber_length(pb, 72); + + mxf_write_local_tag(pb, 16, 0x3C0A); // Instance UID + mxf_write_uuid(pb, EssenceContainerData, 0); + + mxf_write_local_tag(pb, 32, 0x2701); // Linked Package UID + mxf_write_umid(s, 1); + + mxf_write_local_tag(pb, 4, 0x3F07); // BodySID + avio_wb32(pb, 1); + + mxf_write_local_tag(pb, 4, 0x3F06); // IndexSID + avio_wb32(pb, 2); + + return 0; +} + +static int mxf_write_header_metadata_sets(AVFormatContext *s) +{ + mxf_write_preface(s); + mxf_write_identification(s); + mxf_write_content_storage(s); + mxf_write_package(s, MaterialPackage); + mxf_write_package(s, SourcePackage); + mxf_write_essence_container_data(s); + return 0; +} + +static unsigned klv_fill_size(uint64_t size) +{ + unsigned pad = KAG_SIZE - (size & (KAG_SIZE-1)); + if (pad < 20) // smallest fill item possible + return pad + KAG_SIZE; + else + return pad & (KAG_SIZE-1); +} + +static void mxf_write_index_table_segment(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int i, j, temporal_reordering = 0; + int key_index = mxf->last_key_index; + + av_log(s, AV_LOG_DEBUG, "edit units count %d\n", mxf->edit_units_count); + + if (!mxf->edit_units_count && !mxf->edit_unit_byte_count) + return; + + avio_write(pb, index_table_segment_key, 16); + + if (mxf->edit_unit_byte_count) { + klv_encode_ber_length(pb, 80); + } else { + klv_encode_ber_length(pb, 85 + 12+(s->nb_streams+1LL)*6 + + 12+mxf->edit_units_count*(11+mxf->slice_count*4LL)); + } + + // instance id + mxf_write_local_tag(pb, 16, 0x3C0A); + mxf_write_uuid(pb, IndexTableSegment, 0); + + // index edit rate + mxf_write_local_tag(pb, 8, 0x3F0B); + avio_wb32(pb, mxf->time_base.den); + avio_wb32(pb, mxf->time_base.num); + + // index start position + mxf_write_local_tag(pb, 8, 0x3F0C); + avio_wb64(pb, mxf->last_indexed_edit_unit); + + // index duration + mxf_write_local_tag(pb, 8, 0x3F0D); + if (mxf->edit_unit_byte_count) + avio_wb64(pb, 0); // index table covers whole container + else + avio_wb64(pb, mxf->edit_units_count); + + // edit unit byte count + mxf_write_local_tag(pb, 4, 0x3F05); + avio_wb32(pb, mxf->edit_unit_byte_count); + + // index sid + mxf_write_local_tag(pb, 4, 0x3F06); + avio_wb32(pb, 2); + + // body sid + mxf_write_local_tag(pb, 4, 0x3F07); + avio_wb32(pb, 1); + + if (!mxf->edit_unit_byte_count) { + // real slice count - 1 + mxf_write_local_tag(pb, 1, 0x3F08); + avio_w8(pb, mxf->slice_count); + + // delta entry array + mxf_write_local_tag(pb, 8 + (s->nb_streams+1)*6, 0x3F09); + avio_wb32(pb, s->nb_streams+1); // num of entries + avio_wb32(pb, 6); // size of one entry + // write system item delta entry + avio_w8(pb, 0); + avio_w8(pb, 0); // slice entry + avio_wb32(pb, 0); // element delta + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + avio_w8(pb, sc->temporal_reordering); + if (sc->temporal_reordering) + temporal_reordering = 1; + if (i == 0) { // video track + avio_w8(pb, 0); // slice number + avio_wb32(pb, KAG_SIZE); // system item size including klv fill + } else { // audio track + unsigned audio_frame_size = sc->aic.samples[0]*sc->aic.sample_size; + audio_frame_size += klv_fill_size(audio_frame_size); + avio_w8(pb, 1); + avio_wb32(pb, (i-1)*audio_frame_size); // element delta + } + } + + mxf_write_local_tag(pb, 8 + mxf->edit_units_count*(11+mxf->slice_count*4), 0x3F0A); + avio_wb32(pb, mxf->edit_units_count); // num of entries + avio_wb32(pb, 11+mxf->slice_count*4); // size of one entry + + for (i = 0; i < mxf->edit_units_count; i++) { + int temporal_offset = 0; + + if (!(mxf->index_entries[i].flags & 0x33)) { // I frame + mxf->last_key_index = key_index; + key_index = i; + } + + if (temporal_reordering) { + int pic_num_in_gop = i - key_index; + if (pic_num_in_gop != mxf->index_entries[i].temporal_ref) { + for (j = key_index; j < mxf->edit_units_count; j++) { + if (pic_num_in_gop == mxf->index_entries[j].temporal_ref) + break; + } + if (j == mxf->edit_units_count) + av_log(s, AV_LOG_WARNING, "missing frames\n"); + temporal_offset = j - key_index - pic_num_in_gop; + } + } + avio_w8(pb, temporal_offset); + + if ((mxf->index_entries[i].flags & 0x30) == 0x30) { // back and forward prediction + avio_w8(pb, mxf->last_key_index - i); + } else { + avio_w8(pb, key_index - i); // key frame offset + if ((mxf->index_entries[i].flags & 0x20) == 0x20) // only forward + mxf->last_key_index = key_index; + } + + if (!(mxf->index_entries[i].flags & 0x33) && // I frame + mxf->index_entries[i].flags & 0x40 && !temporal_offset) + mxf->index_entries[i].flags |= 0x80; // random access + avio_w8(pb, mxf->index_entries[i].flags); + // stream offset + avio_wb64(pb, mxf->index_entries[i].offset); + if (s->nb_streams > 1) + avio_wb32(pb, mxf->index_entries[i].slice_offset); + } + + mxf->last_key_index = key_index - mxf->edit_units_count; + mxf->last_indexed_edit_unit += mxf->edit_units_count; + mxf->edit_units_count = 0; + } +} + +static void mxf_write_klv_fill(AVFormatContext *s) +{ + unsigned pad = klv_fill_size(avio_tell(s->pb)); + if (pad) { + avio_write(s->pb, klv_fill_key, 16); + pad -= 16 + 4; + klv_encode_ber4_length(s->pb, pad); + for (; pad; pad--) + avio_w8(s->pb, 0); + av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1))); + } +} + +static void mxf_write_partition(AVFormatContext *s, int bodysid, + int indexsid, + const uint8_t *key, int write_metadata) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int64_t header_byte_count_offset; + unsigned index_byte_count = 0; + uint64_t partition_offset = avio_tell(pb); + + if (!mxf->edit_unit_byte_count && mxf->edit_units_count) + index_byte_count = 85 + 12+(s->nb_streams+1)*6 + + 12+mxf->edit_units_count*(11+mxf->slice_count*4); + else if (mxf->edit_unit_byte_count && indexsid) + index_byte_count = 80; + + if (index_byte_count) { + // add encoded ber length + index_byte_count += 16 + klv_ber_length(index_byte_count); + index_byte_count += klv_fill_size(index_byte_count); + } + + if (!memcmp(key, body_partition_key, 16)) { + mxf->body_partition_offset = + av_realloc(mxf->body_partition_offset, + (mxf->body_partitions_count+1)* + sizeof(*mxf->body_partition_offset)); + mxf->body_partition_offset[mxf->body_partitions_count++] = partition_offset; + } + + // write klv + avio_write(pb, key, 16); + klv_encode_ber_length(pb, 88 + 16LL * DESCRIPTOR_COUNT(mxf->essence_container_count)); + + // write partition value + avio_wb16(pb, 1); // majorVersion + avio_wb16(pb, 2); // minorVersion + avio_wb32(pb, KAG_SIZE); // KAGSize + + avio_wb64(pb, partition_offset); // ThisPartition + + if (!memcmp(key, body_partition_key, 16) && mxf->body_partitions_count > 1) + avio_wb64(pb, mxf->body_partition_offset[mxf->body_partitions_count-2]); // PreviousPartition + else if (!memcmp(key, footer_partition_key, 16) && mxf->body_partitions_count) + avio_wb64(pb, mxf->body_partition_offset[mxf->body_partitions_count-1]); // PreviousPartition + else + avio_wb64(pb, 0); + + avio_wb64(pb, mxf->footer_partition_offset); // footerPartition + + // set offset + header_byte_count_offset = avio_tell(pb); + avio_wb64(pb, 0); // headerByteCount, update later + + // indexTable + avio_wb64(pb, index_byte_count); // indexByteCount + avio_wb32(pb, index_byte_count ? indexsid : 0); // indexSID + + // BodyOffset + if (bodysid && mxf->edit_units_count && mxf->body_partitions_count) { + avio_wb64(pb, mxf->body_offset); + } else + avio_wb64(pb, 0); + + avio_wb32(pb, bodysid); // bodySID + + // operational pattern + avio_write(pb, op1a_ul, 16); + + // essence container + mxf_write_essence_container_refs(s); + + if (write_metadata) { + // mark the start of the headermetadata and calculate metadata size + int64_t pos, start; + unsigned header_byte_count; + + mxf_write_klv_fill(s); + start = avio_tell(s->pb); + mxf_write_primer_pack(s); + mxf_write_header_metadata_sets(s); + pos = avio_tell(s->pb); + header_byte_count = pos - start + klv_fill_size(pos); + + // update header_byte_count + avio_seek(pb, header_byte_count_offset, SEEK_SET); + avio_wb64(pb, header_byte_count); + avio_seek(pb, pos, SEEK_SET); + } + + avio_flush(pb); +} + +static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, +AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + int i, cid; + uint8_t* header_cid; + int frame_size = 0; + + if (mxf->header_written) + return 1; + + if (pkt->size < 43) + return -1; + + header_cid = pkt->data + 0x28; + cid = header_cid[0] << 24 | header_cid[1] << 16 | header_cid[2] << 8 | header_cid[3]; + + if ((frame_size = avpriv_dnxhd_get_frame_size(cid)) < 0) + return -1; + + switch (cid) { + case 1235: + sc->index = 24; + sc->component_depth = 10; + break; + case 1237: + sc->index = 25; + break; + case 1238: + sc->index = 26; + break; + case 1241: + sc->index = 27; + sc->component_depth = 10; + break; + case 1242: + sc->index = 28; + break; + case 1243: + sc->index = 29; + break; + case 1250: + sc->index = 30; + sc->component_depth = 10; + break; + case 1251: + sc->index = 31; + break; + case 1252: + sc->index = 32; + break; + case 1253: + sc->index = 33; + break; + default: + return -1; + } + + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + sc->aspect_ratio = (AVRational){ 16, 9 }; + + mxf->edit_unit_byte_count = KAG_SIZE; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mxf->edit_unit_byte_count += 16 + 4 + sc->aic.samples[0]*sc->aic.sample_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + mxf->edit_unit_byte_count += 16 + 4 + frame_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } + } + + return 1; +} + +static int mxf_parse_dv_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + uint8_t *vs_pack, *vsc_pack; + int i, ul_index, frame_size, stype, pal; + + if (mxf->header_written) + return 1; + + // Check for minimal frame size + if (pkt->size < 120000) + return -1; + + vs_pack = pkt->data + 80*5 + 48; + vsc_pack = pkt->data + 80*5 + 53; + stype = vs_pack[3] & 0x1f; + pal = (vs_pack[3] >> 5) & 0x1; + + if ((vs_pack[2] & 0x07) == 0x02) + sc->aspect_ratio = (AVRational){ 16, 9 }; + else + sc->aspect_ratio = (AVRational){ 4, 3 }; + + sc->interlaced = (vsc_pack[3] >> 4) & 0x01; + // TODO: fix dv encoder to set proper FF/FS value in VSC pack + // and set field dominance accordingly + // av_log(s, AV_LOG_DEBUG, "DV vsc pack ff/ss = %x\n", vsc_pack[2] >> 6); + + switch (stype) { + case 0x18: // DV100 720p + ul_index = 6 + pal; + frame_size = pal ? 288000 : 240000; + if (sc->interlaced) { + av_log(s, AV_LOG_ERROR, "source marked as interlaced but codec profile is progressive\n"); + sc->interlaced = 0; + } + break; + case 0x14: // DV100 1080i + ul_index = 4 + pal; + frame_size = pal ? 576000 : 480000; + break; + case 0x04: // DV50 + ul_index = 2 + pal; + frame_size = pal ? 288000 : 240000; + break; + default: // DV25 + ul_index = 0 + pal; + frame_size = pal ? 144000 : 120000; + } + + sc->index = ul_index + 16; + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + + mxf->edit_unit_byte_count = KAG_SIZE; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = st->priv_data; + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + mxf->edit_unit_byte_count += 16 + 4 + sc->aic.samples[0]*sc->aic.sample_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + mxf->edit_unit_byte_count += 16 + 4 + frame_size; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } + } + + return 1; +} + +static const UID mxf_mpeg2_codec_uls[] = { + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x02,0x02,0x00 }, // 422P-ML I-Frame + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x02,0x03,0x00 }, // 422P-ML Long GOP + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x03,0x02,0x00 }, // MP-HL I-Frame + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x03,0x03,0x00 }, // MP-HL Long GOP + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x04,0x02,0x00 }, // 422P-HL I-Frame + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x04,0x03,0x00 }, // 422P-HL Long GOP + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x05,0x02,0x00 }, // MP@H-14 I-Frame + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x05,0x03,0x00 }, // MP@H-14 Long GOP +}; + +static const UID *mxf_get_mpeg2_codec_ul(AVCodecContext *avctx) +{ + int long_gop = avctx->gop_size > 1 || avctx->has_b_frames; + + if (avctx->profile == 4) { // Main + if (avctx->level == 8) // Main + return &mxf_mpeg2_codec_uls[0+long_gop]; + else if (avctx->level == 4) // High + return &mxf_mpeg2_codec_uls[4+long_gop]; + else if (avctx->level == 6) // High 14 + return &mxf_mpeg2_codec_uls[8+long_gop]; + } else if (avctx->profile == 0) { // 422 + if (avctx->level == 5) // Main + return &mxf_mpeg2_codec_uls[2+long_gop]; + else if (avctx->level == 2) // High + return &mxf_mpeg2_codec_uls[6+long_gop]; + } + return NULL; +} + +static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, + AVPacket *pkt, MXFIndexEntry *e) +{ + MXFStreamContext *sc = st->priv_data; + uint32_t c = -1; + int i; + + for(i = 0; i < pkt->size - 4; i++) { + c = (c<<8) + pkt->data[i]; + if (c == 0x1b5) { + if ((pkt->data[i+1] & 0xf0) == 0x10) { // seq ext + st->codec->profile = pkt->data[i+1] & 0x07; + st->codec->level = pkt->data[i+2] >> 4; + } else if (i + 5 < pkt->size && (pkt->data[i+1] & 0xf0) == 0x80) { // pict coding ext + sc->interlaced = !(pkt->data[i+5] & 0x80); // progressive frame + if (sc->interlaced) + sc->field_dominance = 1 + !(pkt->data[i+4] & 0x80); // top field first + break; + } + } else if (c == 0x1b8) { // gop + if (pkt->data[i+4]>>6 & 0x01) { // closed + sc->closed_gop = 1; + if (e->flags & 0x40) // sequence header present + e->flags |= 0x80; // random access + } + } else if (c == 0x1b3) { // seq + e->flags |= 0x40; + switch ((pkt->data[i+4]>>4) & 0xf) { + case 2: sc->aspect_ratio = (AVRational){ 4, 3}; break; + case 3: sc->aspect_ratio = (AVRational){ 16, 9}; break; + case 4: sc->aspect_ratio = (AVRational){221,100}; break; + default: + av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, + st->codec->width, st->codec->height, 1024*1024); + } + } else if (c == 0x100) { // pic + int pict_type = (pkt->data[i+2]>>3) & 0x07; + e->temporal_ref = (pkt->data[i+1]<<2) | (pkt->data[i+2]>>6); + if (pict_type == 2) { // P frame + e->flags |= 0x22; + sc->closed_gop = 0; // reset closed gop, don't matter anymore + } else if (pict_type == 3) { // B frame + if (sc->closed_gop) + e->flags |= 0x13; // only backward prediction + else + e->flags |= 0x33; + sc->temporal_reordering = -1; + } else if (!pict_type) { + av_log(s, AV_LOG_ERROR, "error parsing mpeg2 frame\n"); + return 0; + } + } + } + if (s->oformat != &ff_mxf_d10_muxer) + sc->codec_ul = mxf_get_mpeg2_codec_ul(st->codec); + return !!sc->codec_ul; +} + +static uint64_t mxf_parse_timestamp(time_t timestamp) +{ + struct tm *time = gmtime(×tamp); + if (!time) + return 0; + return (uint64_t)(time->tm_year+1900) << 48 | + (uint64_t)(time->tm_mon+1) << 40 | + (uint64_t) time->tm_mday << 32 | + time->tm_hour << 24 | + time->tm_min << 16 | + time->tm_sec << 8; +} + +static void mxf_gen_umid(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + uint32_t seed = av_get_random_seed(); + uint64_t umid = seed + 0x5294713400000000LL; + + AV_WB64(mxf->umid , umid); + AV_WB64(mxf->umid+8, umid>>8); + + mxf->instance_number = seed; +} + +static int mxf_write_header(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + int i, ret; + uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; + const MXFSamplesPerFrame *spf = NULL; + AVDictionaryEntry *t; + int64_t timestamp = 0; + AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + + if (!s->nb_streams) + return -1; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + MXFStreamContext *sc = av_mallocz(sizeof(*sc)); + if (!sc) + return AVERROR(ENOMEM); + st->priv_data = sc; + + if ((i == 0) ^ (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n"); + return -1; + } + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + AVRational rate, tbc = st->codec->time_base; + // Default component depth to 8 + sc->component_depth = 8; + mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; + spf = ff_mxf_get_samples_per_frame(s, tbc); + if (!spf) { + av_log(s, AV_LOG_ERROR, "Unsupported video frame rate %d/%d\n", + tbc.den, tbc.num); + return AVERROR(EINVAL); + } + mxf->time_base = spf->time_base; + rate = av_inv_q(mxf->time_base); + avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); + if (!tcr) + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + if (tcr) + ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s); + else + ret = av_timecode_init(&mxf->tc, rate, 0, 0, s); + if (ret < 0) + return ret; + if (s->oformat == &ff_mxf_d10_muxer) { + if (st->codec->bit_rate == 50000000) { + if (mxf->time_base.den == 25) sc->index = 3; + else sc->index = 5; + } else if (st->codec->bit_rate == 40000000) { + if (mxf->time_base.den == 25) sc->index = 7; + else sc->index = 9; + } else if (st->codec->bit_rate == 30000000) { + if (mxf->time_base.den == 25) sc->index = 11; + else sc->index = 13; + } else { + av_log(s, AV_LOG_ERROR, "error MXF D-10 only support 30/40/50 mbit/s\n"); + return -1; + } + + mxf->edit_unit_byte_count = KAG_SIZE; // system element + mxf->edit_unit_byte_count += 16 + 4 + (uint64_t)st->codec->bit_rate * + mxf->time_base.num / (8*mxf->time_base.den); + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + mxf->edit_unit_byte_count += 16 + 4 + 4 + spf->samples_per_frame[0]*8*4; + mxf->edit_unit_byte_count += klv_fill_size(mxf->edit_unit_byte_count); + } + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st->codec->sample_rate != 48000) { + av_log(s, AV_LOG_ERROR, "only 48khz is implemented\n"); + return -1; + } + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + if (s->oformat == &ff_mxf_d10_muxer) { + if (st->index != 1) { + av_log(s, AV_LOG_ERROR, "MXF D-10 only support one audio track\n"); + return -1; + } + if (st->codec->codec_id != AV_CODEC_ID_PCM_S16LE && + st->codec->codec_id != AV_CODEC_ID_PCM_S24LE) { + av_log(s, AV_LOG_ERROR, "MXF D-10 only support 16 or 24 bits le audio\n"); + } + sc->index = ((MXFStreamContext*)s->streams[0]->priv_data)->index + 1; + } else + mxf->slice_count = 1; + } + + if (!sc->index) { + sc->index = mxf_get_essence_container_ul_index(st->codec->codec_id); + if (sc->index == -1) { + av_log(s, AV_LOG_ERROR, "track %d: could not find essence container ul, " + "codec not currently supported in container\n", i); + return -1; + } + } + + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + + memcpy(sc->track_essence_element_key, mxf_essence_container_uls[sc->index].element_ul, 15); + sc->track_essence_element_key[15] = present[sc->index]; + PRINT_KEY(s, "track essence element key", sc->track_essence_element_key); + + if (!present[sc->index]) + mxf->essence_container_count++; + present[sc->index]++; + } + + if (s->oformat == &ff_mxf_d10_muxer) { + mxf->essence_container_count = 1; + } + + if (!(s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)) + mxf_gen_umid(s); + + for (i = 0; i < s->nb_streams; i++) { + MXFStreamContext *sc = s->streams[i]->priv_data; + // update element count + sc->track_essence_element_key[13] = present[sc->index]; + if (!memcmp(sc->track_essence_element_key, mxf_essence_container_uls[15].element_ul, 13)) // DV + sc->order = (0x15 << 24) | AV_RB32(sc->track_essence_element_key+13); + else + sc->order = AV_RB32(sc->track_essence_element_key+12); + } + + if (t = av_dict_get(s->metadata, "creation_time", NULL, 0)) + timestamp = ff_iso8601_to_unix_time(t->value); + if (timestamp) + mxf->timestamp = mxf_parse_timestamp(timestamp); + mxf->duration = -1; + + mxf->timecode_track = av_mallocz(sizeof(*mxf->timecode_track)); + if (!mxf->timecode_track) + return AVERROR(ENOMEM); + mxf->timecode_track->priv_data = av_mallocz(sizeof(MXFStreamContext)); + if (!mxf->timecode_track->priv_data) + return AVERROR(ENOMEM); + mxf->timecode_track->index = -1; + + if (!spf) + spf = ff_mxf_get_samples_per_frame(s, (AVRational){ 1, 25 }); + + if (ff_audio_interleave_init(s, spf->samples_per_frame, mxf->time_base) < 0) + return -1; + + return 0; +} + +static const uint8_t system_metadata_pack_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x01,0x00 }; +static const uint8_t system_metadata_package_set_key[] = { 0x06,0x0E,0x2B,0x34,0x02,0x43,0x01,0x01,0x0D,0x01,0x03,0x01,0x04,0x01,0x02,0x01 }; + +static void mxf_write_system_item(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + unsigned frame; + uint32_t time_code; + + frame = mxf->last_indexed_edit_unit + mxf->edit_units_count; + + // write system metadata pack + avio_write(pb, system_metadata_pack_key, 16); + klv_encode_ber4_length(pb, 57); + avio_w8(pb, 0x5c); // UL, user date/time stamp, picture and sound item present + avio_w8(pb, 0x04); // content package rate + avio_w8(pb, 0x00); // content package type + avio_wb16(pb, 0x00); // channel handle + avio_wb16(pb, (mxf->tc.start + frame) & 0xFFFF); // continuity count, supposed to overflow + if (mxf->essence_container_count > 1) + avio_write(pb, multiple_desc_ul, 16); + else { + MXFStreamContext *sc = s->streams[0]->priv_data; + avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); + } + avio_w8(pb, 0); + avio_wb64(pb, 0); + avio_wb64(pb, 0); // creation date/time stamp + + avio_w8(pb, 0x81); // SMPTE 12M time code + time_code = av_timecode_get_smpte_from_framenum(&mxf->tc, frame); + avio_wb32(pb, time_code); + avio_wb32(pb, 0); // binary group data + avio_wb64(pb, 0); + + // write system metadata package set + avio_write(pb, system_metadata_package_set_key, 16); + klv_encode_ber4_length(pb, 35); + avio_w8(pb, 0x83); // UMID + avio_wb16(pb, 0x20); + mxf_write_umid(s, 1); +} + +static void mxf_write_d10_video_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int packet_size = (uint64_t)st->codec->bit_rate*mxf->time_base.num / + (8*mxf->time_base.den); // frame size + int pad; + + packet_size += 16 + 4; + packet_size += klv_fill_size(packet_size); + + klv_encode_ber4_length(pb, pkt->size); + avio_write(pb, pkt->data, pkt->size); + + // ensure CBR muxing by padding to correct video frame size + pad = packet_size - pkt->size - 16 - 4; + if (pad > 20) { + avio_write(s->pb, klv_fill_key, 16); + pad -= 16 + 4; + klv_encode_ber4_length(s->pb, pad); + for (; pad; pad--) + avio_w8(s->pb, 0); + av_assert1(!(avio_tell(s->pb) & (KAG_SIZE-1))); + } else { + av_log(s, AV_LOG_WARNING, "cannot fill d-10 video packet\n"); + for (; pad > 0; pad--) + avio_w8(s->pb, 0); + } +} + +static void mxf_write_d10_audio_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + int frame_size = pkt->size / st->codec->block_align; + uint8_t *samples = pkt->data; + uint8_t *end = pkt->data + pkt->size; + int i; + + klv_encode_ber4_length(pb, 4 + frame_size*4*8); + + avio_w8(pb, (frame_size == 1920 ? 0 : (mxf->edit_units_count-1) % 5 + 1)); + avio_wl16(pb, frame_size); + avio_w8(pb, (1<codec->channels)-1); + + while (samples < end) { + for (i = 0; i < st->codec->channels; i++) { + uint32_t sample; + if (st->codec->codec_id == AV_CODEC_ID_PCM_S24LE) { + sample = AV_RL24(samples)<< 4; + samples += 3; + } else { + sample = AV_RL16(samples)<<12; + samples += 2; + } + avio_wl32(pb, sample | i); + } + for (; i < 8; i++) + avio_wl32(pb, i); + } +} + +static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[pkt->stream_index]; + MXFStreamContext *sc = st->priv_data; + MXFIndexEntry ie = {0}; + + if (!mxf->edit_unit_byte_count && !(mxf->edit_units_count % EDIT_UNITS_PER_BODY)) { + mxf->index_entries = av_realloc(mxf->index_entries, + (mxf->edit_units_count + EDIT_UNITS_PER_BODY)*sizeof(*mxf->index_entries)); + if (!mxf->index_entries) { + av_log(s, AV_LOG_ERROR, "could not allocate index entries\n"); + return -1; + } + } + + if (st->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + if (!mxf_parse_mpeg2_frame(s, st, pkt, &ie)) { + av_log(s, AV_LOG_ERROR, "could not get mpeg2 profile and level\n"); + return -1; + } + } else if (st->codec->codec_id == AV_CODEC_ID_DNXHD) { + if (!mxf_parse_dnxhd_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get dnxhd profile\n"); + return -1; + } + } else if (st->codec->codec_id == AV_CODEC_ID_DVVIDEO) { + if (!mxf_parse_dv_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get dv profile\n"); + return -1; + } + } + + if (!mxf->header_written) { + if (mxf->edit_unit_byte_count) { + mxf_write_partition(s, 1, 2, header_open_partition_key, 1); + mxf_write_klv_fill(s); + mxf_write_index_table_segment(s); + } else { + mxf_write_partition(s, 0, 0, header_open_partition_key, 1); + } + mxf->header_written = 1; + } + + if (st->index == 0) { + if (!mxf->edit_unit_byte_count && + (!mxf->edit_units_count || mxf->edit_units_count > EDIT_UNITS_PER_BODY) && + !(ie.flags & 0x33)) { // I frame, Gop start + mxf_write_klv_fill(s); + mxf_write_partition(s, 1, 2, body_partition_key, 0); + + mxf_write_klv_fill(s); + mxf_write_index_table_segment(s); + } + + mxf_write_klv_fill(s); + mxf_write_system_item(s); + + if (!mxf->edit_unit_byte_count) { + mxf->index_entries[mxf->edit_units_count].offset = mxf->body_offset; + mxf->index_entries[mxf->edit_units_count].flags = ie.flags; + mxf->index_entries[mxf->edit_units_count].temporal_ref = ie.temporal_ref; + mxf->body_offset += KAG_SIZE; // size of system element + } + mxf->edit_units_count++; + } else if (!mxf->edit_unit_byte_count && st->index == 1) { + mxf->index_entries[mxf->edit_units_count-1].slice_offset = + mxf->body_offset - mxf->index_entries[mxf->edit_units_count-1].offset; + } + + mxf_write_klv_fill(s); + avio_write(pb, sc->track_essence_element_key, 16); // write key + if (s->oformat == &ff_mxf_d10_muxer) { + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + mxf_write_d10_video_packet(s, st, pkt); + else + mxf_write_d10_audio_packet(s, st, pkt); + } else { + klv_encode_ber4_length(pb, pkt->size); // write length + avio_write(pb, pkt->data, pkt->size); + mxf->body_offset += 16+4+pkt->size + klv_fill_size(16+4+pkt->size); + } + + avio_flush(pb); + + return 0; +} + +static void mxf_write_random_index_pack(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + uint64_t pos = avio_tell(pb); + int i; + + avio_write(pb, random_index_pack_key, 16); + klv_encode_ber_length(pb, 28 + 12LL*mxf->body_partitions_count); + + if (mxf->edit_unit_byte_count) + avio_wb32(pb, 1); // BodySID of header partition + else + avio_wb32(pb, 0); + avio_wb64(pb, 0); // offset of header partition + + for (i = 0; i < mxf->body_partitions_count; i++) { + avio_wb32(pb, 1); // BodySID + avio_wb64(pb, mxf->body_partition_offset[i]); + } + + avio_wb32(pb, 0); // BodySID of footer partition + avio_wb64(pb, mxf->footer_partition_offset); + + avio_wb32(pb, avio_tell(pb) - pos + 4); +} + +static int mxf_write_footer(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; + AVIOContext *pb = s->pb; + + mxf->duration = mxf->last_indexed_edit_unit + mxf->edit_units_count; + + mxf_write_klv_fill(s); + mxf->footer_partition_offset = avio_tell(pb); + if (mxf->edit_unit_byte_count) { // no need to repeat index + mxf_write_partition(s, 0, 0, footer_partition_key, 0); + } else { + mxf_write_partition(s, 0, 2, footer_partition_key, 0); + + mxf_write_klv_fill(s); + mxf_write_index_table_segment(s); + } + + mxf_write_klv_fill(s); + mxf_write_random_index_pack(s); + + if (s->pb->seekable) { + avio_seek(pb, 0, SEEK_SET); + if (mxf->edit_unit_byte_count) { + mxf_write_partition(s, 1, 2, header_closed_partition_key, 1); + mxf_write_klv_fill(s); + mxf_write_index_table_segment(s); + } else { + mxf_write_partition(s, 0, 0, header_closed_partition_key, 1); + } + } + + ff_audio_interleave_close(s); + + av_freep(&mxf->index_entries); + av_freep(&mxf->body_partition_offset); + av_freep(&mxf->timecode_track->priv_data); + av_freep(&mxf->timecode_track); + + mxf_free(s); + + return 0; +} + +static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + int i, stream_count = 0; + + for (i = 0; i < s->nb_streams; i++) + stream_count += !!s->streams[i]->last_in_packet_buffer; + + if (stream_count && (s->nb_streams == stream_count || flush)) { + AVPacketList *pktl = s->packet_buffer; + if (s->nb_streams != stream_count) { + AVPacketList *last = NULL; + // find last packet in edit unit + while (pktl) { + if (!stream_count || pktl->pkt.stream_index == 0) + break; + last = pktl; + pktl = pktl->next; + stream_count--; + } + // purge packet queue + while (pktl) { + AVPacketList *next = pktl->next; + + if(s->streams[pktl->pkt.stream_index]->last_in_packet_buffer == pktl) + s->streams[pktl->pkt.stream_index]->last_in_packet_buffer= NULL; + av_free_packet(&pktl->pkt); + av_freep(&pktl); + pktl = next; + } + if (last) + last->next = NULL; + else { + s->packet_buffer = NULL; + s->packet_buffer_end= NULL; + goto out; + } + pktl = s->packet_buffer; + } + + *out = pktl->pkt; + av_dlog(s, "out st:%d dts:%"PRId64"\n", (*out).stream_index, (*out).dts); + s->packet_buffer = pktl->next; + if(s->streams[pktl->pkt.stream_index]->last_in_packet_buffer == pktl) + s->streams[pktl->pkt.stream_index]->last_in_packet_buffer= NULL; + if(!s->packet_buffer) + s->packet_buffer_end= NULL; + av_freep(&pktl); + return 1; + } else { + out: + av_init_packet(out); + return 0; + } +} + +static int mxf_compare_timestamps(AVFormatContext *s, AVPacket *next, AVPacket *pkt) +{ + MXFStreamContext *sc = s->streams[pkt ->stream_index]->priv_data; + MXFStreamContext *sc2 = s->streams[next->stream_index]->priv_data; + + return next->dts > pkt->dts || + (next->dts == pkt->dts && sc->order < sc2->order); +} + +static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) +{ + return ff_audio_rechunk_interleave(s, out, pkt, flush, + mxf_interleave_get_packet, mxf_compare_timestamps); +} + +AVOutputFormat ff_mxf_muxer = { + .name = "mxf", + .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), + .mime_type = "application/mxf", + .extensions = "mxf", + .priv_data_size = sizeof(MXFContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mxf_write_header, + .write_packet = mxf_write_packet, + .write_trailer = mxf_write_footer, + .flags = AVFMT_NOTIMESTAMPS, + .interleave_packet = mxf_interleave, +}; + +AVOutputFormat ff_mxf_d10_muxer = { + .name = "mxf_d10", + .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format) D-10 Mapping"), + .mime_type = "application/mxf", + .priv_data_size = sizeof(MXFContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_header = mxf_write_header, + .write_packet = mxf_write_packet, + .write_trailer = mxf_write_footer, + .flags = AVFMT_NOTIMESTAMPS, + .interleave_packet = mxf_interleave, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxfenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/mxfenc.o libavformat/mxfenc.o: libavformat/mxfenc.c \ + libavutil/opt.h libavutil/rational.h libavutil/attributes.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/samplefmt.h \ + libavutil/random_seed.h libavutil/timecode.h libavutil/avassert.h \ + libavcodec/bytestream.h libavutil/common.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavcodec/dnxhddata.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/internal.h \ + libavformat/audiointerleave.h libavutil/fifo.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/mxf.h config.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxfenc.o Binary file ffmpeg/libavformat/mxfenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxg.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,260 @@ +/* + * MxPEG clip file demuxer + * Copyright (c) 2010 Anatoly Nenashev + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavcodec/mjpeg.h" +#include "avformat.h" +#include "internal.h" +#include "avio.h" + +#define DEFAULT_PACKET_SIZE 1024 +#define OVERREAD_SIZE 3 + +typedef struct MXGContext { + uint8_t *buffer; + uint8_t *buffer_ptr; + uint8_t *soi_ptr; + unsigned int buffer_size; + int64_t dts; + unsigned int cache_size; +} MXGContext; + +static int mxg_read_header(AVFormatContext *s) +{ + AVStream *video_st, *audio_st; + MXGContext *mxg = s->priv_data; + + /* video parameters will be extracted from the compressed bitstream */ + video_st = avformat_new_stream(s, NULL); + if (!video_st) + return AVERROR(ENOMEM); + video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + video_st->codec->codec_id = AV_CODEC_ID_MXPEG; + avpriv_set_pts_info(video_st, 64, 1, 1000000); + + audio_st = avformat_new_stream(s, NULL); + if (!audio_st) + return AVERROR(ENOMEM); + audio_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + audio_st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; + audio_st->codec->channels = 1; + audio_st->codec->channel_layout = AV_CH_LAYOUT_MONO; + audio_st->codec->sample_rate = 8000; + audio_st->codec->bits_per_coded_sample = 8; + audio_st->codec->block_align = 1; + avpriv_set_pts_info(audio_st, 64, 1, 1000000); + + mxg->soi_ptr = mxg->buffer_ptr = mxg->buffer = 0; + mxg->buffer_size = 0; + mxg->dts = AV_NOPTS_VALUE; + mxg->cache_size = 0; + + return 0; +} + +static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end) +{ + for (; p < end - 3; p += 4) { + uint32_t x = *(uint32_t*)p; + + if (x & (~(x+0x01010101)) & 0x80808080) { + if (p[0] == 0xff) { + return p; + } else if (p[1] == 0xff) { + return p+1; + } else if (p[2] == 0xff) { + return p+2; + } else if (p[3] == 0xff) { + return p+3; + } + } + } + + for (; p < end; ++p) { + if (*p == 0xff) return p; + } + + return end; +} + +static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size) +{ + MXGContext *mxg = s->priv_data; + unsigned int current_pos = mxg->buffer_ptr - mxg->buffer; + unsigned int soi_pos; + uint8_t *buffer; + int ret; + + /* reallocate internal buffer */ + if (current_pos > current_pos + cache_size) + return AVERROR(ENOMEM); + soi_pos = mxg->soi_ptr - mxg->buffer; + buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size, + current_pos + cache_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if (!buffer) + return AVERROR(ENOMEM); + mxg->buffer = buffer; + mxg->buffer_ptr = mxg->buffer + current_pos; + if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos; + + /* get data */ + ret = avio_read(s->pb, mxg->buffer_ptr + mxg->cache_size, + cache_size - mxg->cache_size); + if (ret < 0) + return ret; + + mxg->cache_size += ret; + + return ret; +} + +static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + unsigned int size; + uint8_t *startmarker_ptr, *end, *search_end, marker; + MXGContext *mxg = s->priv_data; + + while (!url_feof(s->pb) && !s->pb->error){ + if (mxg->cache_size <= OVERREAD_SIZE) { + /* update internal buffer */ + ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE); + if (ret < 0) + return ret; + } + end = mxg->buffer_ptr + mxg->cache_size; + + /* find start marker - 0xff */ + if (mxg->cache_size > OVERREAD_SIZE) { + search_end = end - OVERREAD_SIZE; + startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); + } else { + search_end = end; + startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); + if (startmarker_ptr >= search_end - 1 || + *(startmarker_ptr + 1) != EOI) break; + } + + if (startmarker_ptr != search_end) { /* start marker found */ + marker = *(startmarker_ptr + 1); + mxg->buffer_ptr = startmarker_ptr + 2; + mxg->cache_size = end - mxg->buffer_ptr; + + if (marker == SOI) { + mxg->soi_ptr = startmarker_ptr; + } else if (marker == EOI) { + if (!mxg->soi_ptr) { + av_log(s, AV_LOG_WARNING, "Found EOI before SOI, skipping\n"); + continue; + } + + pkt->pts = pkt->dts = mxg->dts; + pkt->stream_index = 0; +#if FF_API_DESTRUCT_PACKET + pkt->destruct = NULL; +#endif + pkt->buf = NULL; + pkt->size = mxg->buffer_ptr - mxg->soi_ptr; + pkt->data = mxg->soi_ptr; + + if (mxg->soi_ptr - mxg->buffer > mxg->cache_size) { + if (mxg->cache_size > 0) { + memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); + } + + mxg->buffer_ptr = mxg->buffer; + } + mxg->soi_ptr = 0; + + return pkt->size; + } else if ( (SOF0 <= marker && marker <= SOF15) || + (SOS <= marker && marker <= COM) ) { + /* all other markers that start marker segment also contain + length value (see specification for JPEG Annex B.1) */ + size = AV_RB16(mxg->buffer_ptr); + if (size < 2) + return AVERROR(EINVAL); + + if (mxg->cache_size < size) { + ret = mxg_update_cache(s, size); + if (ret < 0) + return ret; + startmarker_ptr = mxg->buffer_ptr - 2; + mxg->cache_size = 0; + } else { + mxg->cache_size -= size; + } + + mxg->buffer_ptr += size; + + if (marker == APP13 && size >= 16) { /* audio data */ + /* time (GMT) of first sample in usec since 1970, little-endian */ + pkt->pts = pkt->dts = AV_RL64(startmarker_ptr + 8); + pkt->stream_index = 1; +#if FF_API_DESTRUCT_PACKET + pkt->destruct = NULL; +#endif + pkt->buf = NULL; + pkt->size = size - 14; + pkt->data = startmarker_ptr + 16; + + if (startmarker_ptr - mxg->buffer > mxg->cache_size) { + if (mxg->cache_size > 0) { + memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); + } + mxg->buffer_ptr = mxg->buffer; + } + + return pkt->size; + } else if (marker == COM && size >= 18 && + !strncmp(startmarker_ptr + 4, "MXF", 3)) { + /* time (GMT) of video frame in usec since 1970, little-endian */ + mxg->dts = AV_RL64(startmarker_ptr + 12); + } + } + } else { + /* start marker not found */ + mxg->buffer_ptr = search_end; + mxg->cache_size = OVERREAD_SIZE; + } + } + + return AVERROR_EOF; +} + +static int mxg_close(struct AVFormatContext *s) +{ + MXGContext *mxg = s->priv_data; + av_freep(&mxg->buffer); + return 0; +} + +AVInputFormat ff_mxg_demuxer = { + .name = "mxg", + .long_name = NULL_IF_CONFIG_SMALL("MxPEG clip"), + .priv_data_size = sizeof(MXGContext), + .read_header = mxg_read_header, + .read_packet = mxg_read_packet, + .read_close = mxg_close, + .extensions = "mxg", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxg.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/mxg.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/mxg.o libavformat/mxg.o: libavformat/mxg.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavcodec/mjpeg.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavcodec/put_bits.h \ + libavutil/bswap.h libavutil/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/mxg.o Binary file ffmpeg/libavformat/mxg.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ncdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ncdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,101 @@ +/* + * NC camera feed demuxer + * Copyright (c) 2009 Nicolas Martin (martinic at iro dot umontreal dot ca) + * Edouard Auvinet + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define NC_VIDEO_FLAG 0x1A5 + +static int nc_probe(AVProbeData *probe_packet) +{ + int size; + + if (AV_RB32(probe_packet->buf) != NC_VIDEO_FLAG) + return 0; + + size = AV_RL16(probe_packet->buf + 5); + + if (size + 20 > probe_packet->buf_size) + return AVPROBE_SCORE_MAX/4; + + if (AV_RB32(probe_packet->buf+16+size) == NC_VIDEO_FLAG) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int nc_read_header(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG4; + st->need_parsing = AVSTREAM_PARSE_FULL; + + avpriv_set_pts_info(st, 64, 1, 100); + + return 0; +} + +static int nc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int size; + int ret; + + uint32_t state=-1; + while (state != NC_VIDEO_FLAG) { + if (url_feof(s->pb)) + return AVERROR(EIO); + state = (state<<8) + avio_r8(s->pb); + } + + avio_r8(s->pb); + size = avio_rl16(s->pb); + avio_skip(s->pb, 9); + + if (size == 0) { + av_log(s, AV_LOG_DEBUG, "Next packet size is zero\n"); + return AVERROR(EAGAIN); + } + + ret = av_get_packet(s->pb, pkt, size); + if (ret != size) { + if (ret > 0) av_free_packet(pkt); + return AVERROR(EIO); + } + + pkt->stream_index = 0; + return size; +} + +AVInputFormat ff_nc_demuxer = { + .name = "nc", + .long_name = NULL_IF_CONFIG_SMALL("NC camera feed"), + .read_probe = nc_probe, + .read_header = nc_read_header, + .read_packet = nc_read_packet, + .extensions = "v", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ncdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/ncdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/ncdec.o libavformat/ncdec.o: libavformat/ncdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/ncdec.o Binary file ffmpeg/libavformat/ncdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/network.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/network.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2007 The Libav Project + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avutil.h" +#include "network.h" +#include "libavcodec/internal.h" +#include "libavutil/mem.h" +#include "url.h" +#include "libavutil/time.h" + +#if HAVE_THREADS +#if HAVE_PTHREADS +#include +#elif HAVE_OS2THREADS +#include "libavcodec/os2threads.h" +#else +#include "libavcodec/w32pthreads.h" +#endif +#endif + +#if CONFIG_OPENSSL +#include +static int openssl_init; +#if HAVE_THREADS +#include +#include "libavutil/avutil.h" +pthread_mutex_t *openssl_mutexes; +static void openssl_lock(int mode, int type, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&openssl_mutexes[type]); + else + pthread_mutex_unlock(&openssl_mutexes[type]); +} +#if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000 +static unsigned long openssl_thread_id(void) +{ + return (intptr_t) pthread_self(); +} +#endif +#endif +#endif +#if CONFIG_GNUTLS +#include +#if HAVE_THREADS && GNUTLS_VERSION_NUMBER <= 0x020b00 +#include +#include +GCRY_THREAD_OPTION_PTHREAD_IMPL; +#endif +#endif + +void ff_tls_init(void) +{ + avpriv_lock_avformat(); +#if CONFIG_OPENSSL + if (!openssl_init) { + SSL_library_init(); + SSL_load_error_strings(); +#if HAVE_THREADS + if (!CRYPTO_get_locking_callback()) { + int i; + openssl_mutexes = av_malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); + for (i = 0; i < CRYPTO_num_locks(); i++) + pthread_mutex_init(&openssl_mutexes[i], NULL); + CRYPTO_set_locking_callback(openssl_lock); +#if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000 + CRYPTO_set_id_callback(openssl_thread_id); +#endif + } +#endif + } + openssl_init++; +#endif +#if CONFIG_GNUTLS +#if HAVE_THREADS && GNUTLS_VERSION_NUMBER < 0x020b00 + if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0) + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); +#endif + gnutls_global_init(); +#endif + avpriv_unlock_avformat(); +} + +void ff_tls_deinit(void) +{ + avpriv_lock_avformat(); +#if CONFIG_OPENSSL + openssl_init--; + if (!openssl_init) { +#if HAVE_THREADS + if (CRYPTO_get_locking_callback() == openssl_lock) { + int i; + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) + pthread_mutex_destroy(&openssl_mutexes[i]); + av_free(openssl_mutexes); + } +#endif + } +#endif +#if CONFIG_GNUTLS + gnutls_global_deinit(); +#endif + avpriv_unlock_avformat(); +} + +int ff_network_inited_globally; + +int ff_network_init(void) +{ +#if HAVE_WINSOCK2_H + WSADATA wsaData; +#endif + + if (!ff_network_inited_globally) + av_log(NULL, AV_LOG_WARNING, "Using network protocols without global " + "network initialization. Please use " + "avformat_network_init(), this will " + "become mandatory later.\n"); +#if HAVE_WINSOCK2_H + if (WSAStartup(MAKEWORD(1,1), &wsaData)) + return 0; +#endif + return 1; +} + +int ff_network_wait_fd(int fd, int write) +{ + int ev = write ? POLLOUT : POLLIN; + struct pollfd p = { .fd = fd, .events = ev, .revents = 0 }; + int ret; + ret = poll(&p, 1, 100); + return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); +} + +int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) +{ + int ret; + int64_t wait_start = 0; + + while (1) { + ret = ff_network_wait_fd(fd, write); + if (ret != AVERROR(EAGAIN)) + return ret; + if (ff_check_interrupt(int_cb)) + return AVERROR_EXIT; + if (timeout) { + if (!wait_start) + wait_start = av_gettime(); + else if (av_gettime() - wait_start > timeout) + return AVERROR(ETIMEDOUT); + } + } +} + +void ff_network_close(void) +{ +#if HAVE_WINSOCK2_H + WSACleanup(); +#endif +} + +#if HAVE_WINSOCK2_H +int ff_neterrno(void) +{ + int err = WSAGetLastError(); + switch (err) { + case WSAEWOULDBLOCK: + return AVERROR(EAGAIN); + case WSAEINTR: + return AVERROR(EINTR); + case WSAEPROTONOSUPPORT: + return AVERROR(EPROTONOSUPPORT); + case WSAETIMEDOUT: + return AVERROR(ETIMEDOUT); + case WSAECONNREFUSED: + return AVERROR(ECONNREFUSED); + case WSAEINPROGRESS: + return AVERROR(EINPROGRESS); + } + return -err; +} +#endif + +int ff_is_multicast_address(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) { + return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr)); + } +#if HAVE_STRUCT_SOCKADDR_IN6 + if (addr->sa_family == AF_INET6) { + return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr); + } +#endif + + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/network.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/network.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/network.o libavformat/network.o: libavformat/network.c \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavformat/network.h \ + config.h libavutil/error.h libavformat/os_support.h libavformat/avio.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h \ + libavformat/version.h libavutil/avutil.h libavcodec/internal.h \ + libavutil/buffer.h libavutil/mathematics.h libavutil/pixfmt.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + config.h libavutil/mem.h libavformat/url.h libavutil/time.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/network.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/network.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2007 The FFmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_NETWORK_H +#define AVFORMAT_NETWORK_H + +#include +#include + +#include "config.h" +#include "libavutil/error.h" +#include "os_support.h" +#include "avio.h" + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_WINSOCK2_H +#include +#include + +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +#define getsockopt(a, b, c, d, e) getsockopt(a, b, c, (char*) d, e) +#define setsockopt(a, b, c, d, e) setsockopt(a, b, c, (const char*) d, e) + +int ff_neterrno(void); +#else +#include +#include +#include +#include + +#define ff_neterrno() AVERROR(errno) +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_POLL_H +#include +#endif + +int ff_socket_nonblock(int socket, int enable); + +extern int ff_network_inited_globally; +int ff_network_init(void); +void ff_network_close(void); + +void ff_tls_init(void); +void ff_tls_deinit(void); + +int ff_network_wait_fd(int fd, int write); + +/** + * This works similarly to ff_network_wait_fd, but waits up to 'timeout' microseconds + * Uses ff_network_wait_fd in a loop + * + * @fd Socket descriptor + * @write Set 1 to wait for socket able to be read, 0 to be written + * @timeout Timeout interval, in microseconds. Actual precision is 100000 mcs, due to ff_network_wait_fd usage + * @param int_cb Interrupt callback, is checked after each ff_network_wait_fd call + * @return 0 if data can be read/written, AVERROR(ETIMEDOUT) if timeout expired, or negative error code + */ +int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb); + +int ff_inet_aton (const char * str, struct in_addr * add); + +#if !HAVE_STRUCT_SOCKADDR_STORAGE +struct sockaddr_storage { +#if HAVE_STRUCT_SOCKADDR_SA_LEN + uint8_t ss_len; + uint8_t ss_family; +#else + uint16_t ss_family; +#endif + char ss_pad1[6]; + int64_t ss_align; + char ss_pad2[112]; +}; +#endif + +#if !HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + int ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +#endif + +/* getaddrinfo constants */ +#ifndef EAI_AGAIN +#define EAI_AGAIN 2 +#endif +#ifndef EAI_BADFLAGS +#define EAI_BADFLAGS 3 +#endif +#ifndef EAI_FAIL +#define EAI_FAIL 4 +#endif +#ifndef EAI_FAMILY +#define EAI_FAMILY 5 +#endif +#ifndef EAI_MEMORY +#define EAI_MEMORY 6 +#endif +#ifndef EAI_NODATA +#define EAI_NODATA 7 +#endif +#ifndef EAI_NONAME +#define EAI_NONAME 8 +#endif +#ifndef EAI_SERVICE +#define EAI_SERVICE 9 +#endif +#ifndef EAI_SOCKTYPE +#define EAI_SOCKTYPE 10 +#endif + +#ifndef AI_PASSIVE +#define AI_PASSIVE 1 +#endif + +#ifndef AI_CANONNAME +#define AI_CANONNAME 2 +#endif + +#ifndef AI_NUMERICHOST +#define AI_NUMERICHOST 4 +#endif + +#ifndef NI_NOFQDN +#define NI_NOFQDN 1 +#endif + +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 2 +#endif + +#ifndef NI_NAMERQD +#define NI_NAMERQD 4 +#endif + +#ifndef NI_NUMERICSERV +#define NI_NUMERICSERV 8 +#endif + +#ifndef NI_DGRAM +#define NI_DGRAM 16 +#endif + +#if !HAVE_GETADDRINFO +int ff_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res); +void ff_freeaddrinfo(struct addrinfo *res); +int ff_getnameinfo(const struct sockaddr *sa, int salen, + char *host, int hostlen, + char *serv, int servlen, int flags); +#define getaddrinfo ff_getaddrinfo +#define freeaddrinfo ff_freeaddrinfo +#define getnameinfo ff_getnameinfo +#endif +#if !HAVE_GETADDRINFO || HAVE_WINSOCK2_H +const char *ff_gai_strerror(int ecode); +#undef gai_strerror +#define gai_strerror ff_gai_strerror +#endif + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN INET_ADDRSTRLEN +#endif + +#ifndef IN_MULTICAST +#define IN_MULTICAST(a) ((((uint32_t)(a)) & 0xf0000000) == 0xe0000000) +#endif +#ifndef IN6_IS_ADDR_MULTICAST +#define IN6_IS_ADDR_MULTICAST(a) (((uint8_t *) (a))[0] == 0xff) +#endif + +int ff_is_multicast_address(struct sockaddr *addr); + +#endif /* AVFORMAT_NETWORK_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/network.o Binary file ffmpeg/libavformat/network.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nistspheredec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nistspheredec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,128 @@ +/* + * NIST Sphere demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int nist_probe(AVProbeData *p) +{ + if (AV_RL64(p->buf) == AV_RL64("NIST_1A\x0a")) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int nist_read_header(AVFormatContext *s) +{ + char buffer[32], coding[32] = "pcm", format[32] = "01"; + int bps = 0, be = 0; + int32_t header_size; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + + ff_get_line(s->pb, buffer, sizeof(buffer)); + ff_get_line(s->pb, buffer, sizeof(buffer)); + sscanf(buffer, "%"SCNd32, &header_size); + if (header_size <= 0) + return AVERROR_INVALIDDATA; + + while (!url_feof(s->pb)) { + ff_get_line(s->pb, buffer, sizeof(buffer)); + + if (avio_tell(s->pb) >= header_size) + return AVERROR_INVALIDDATA; + + if (!memcmp(buffer, "end_head", 8)) { + if (!st->codec->bits_per_coded_sample) + st->codec->bits_per_coded_sample = bps << 3; + + if (!av_strcasecmp(coding, "pcm")) { + st->codec->codec_id = ff_get_pcm_codec_id(st->codec->bits_per_coded_sample, + 0, be, 0xFFFF); + } else if (!av_strcasecmp(coding, "alaw")) { + st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; + } else if (!av_strcasecmp(coding, "ulaw") || + !av_strcasecmp(coding, "mu-law")) { + st->codec->codec_id = AV_CODEC_ID_PCM_MULAW; + } else { + avpriv_request_sample(s, "coding %s", coding); + } + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + st->codec->block_align = st->codec->bits_per_coded_sample * st->codec->channels / 8; + + if (avio_tell(s->pb) > header_size) + return AVERROR_INVALIDDATA; + + avio_skip(s->pb, header_size - avio_tell(s->pb)); + + return 0; + } else if (!memcmp(buffer, "channel_count", 13)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->channels); + } else if (!memcmp(buffer, "sample_byte_format", 18)) { + sscanf(buffer, "%*s %*s %31s", format); + + if (!av_strcasecmp(format, "01")) { + be = 0; + } else if (!av_strcasecmp(format, "10")) { + be = 1; + } else if (av_strcasecmp(format, "1")) { + avpriv_request_sample(s, "sample byte format %s", format); + return AVERROR_PATCHWELCOME; + } + } else if (!memcmp(buffer, "sample_coding", 13)) { + sscanf(buffer, "%*s %*s %31s", coding); + } else if (!memcmp(buffer, "sample_count", 12)) { + sscanf(buffer, "%*s %*s %"SCNd64, &st->duration); + } else if (!memcmp(buffer, "sample_n_bytes", 14)) { + sscanf(buffer, "%*s %*s %"SCNd32, &bps); + } else if (!memcmp(buffer, "sample_rate", 11)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->sample_rate); + } else if (!memcmp(buffer, "sample_sig_bits", 15)) { + sscanf(buffer, "%*s %*s %"SCNd32, &st->codec->bits_per_coded_sample); + } else { + char key[32], value[32]; + sscanf(buffer, "%31s %*s %31s", key, value); + av_dict_set(&s->metadata, key, value, AV_DICT_APPEND); + } + } + + return AVERROR_EOF; +} + +AVInputFormat ff_nistsphere_demuxer = { + .name = "nistsphere", + .long_name = NULL_IF_CONFIG_SMALL("NIST SPeech HEader REsources"), + .read_probe = nist_probe, + .read_header = nist_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "nist,sph", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nistspheredec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nistspheredec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/nistspheredec.o libavformat/nistspheredec.o: \ + libavformat/nistspheredec.c libavutil/avstring.h libavutil/attributes.h \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/bswap.h \ + config.h libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nistspheredec.o Binary file ffmpeg/libavformat/nistspheredec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/noproxy-test.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/noproxy-test.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "internal.h" + +static void test(const char *pattern, const char *host) +{ + int res = ff_http_match_no_proxy(pattern, host); + printf("The pattern \"%s\" %s the hostname %s\n", + pattern ? pattern : "(null)", res ? "matches" : "does not match", + host); +} + +int main(void) +{ + test(NULL, "domain.com"); + test("example.com domain.com", "domain.com"); + test("example.com other.com", "domain.com"); + test("example.com,domain.com", "domain.com"); + test("example.com,domain.com", "otherdomain.com"); + test("example.com, *.domain.com", "sub.domain.com"); + test("example.com, *.domain.com", "domain.com"); + test("example.com, .domain.com", "domain.com"); + test("*", "domain.com"); + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nsvdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nsvdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,803 @@ +/* + * NSV demuxer + * Copyright (c) 2004 The FFmpeg Project + * + * first version by Francois Revol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/attributes.h" +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "internal.h" +#include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" + +//#define DEBUG_DUMP_INDEX // XXX dumbdriving-271.nsv breaks with it commented!! +#define CHECK_SUBSEQUENT_NSVS +//#define DISABLE_AUDIO + +/* max bytes to crawl for trying to resync + * stupid streaming servers don't start at chunk boundaries... + */ +#define NSV_MAX_RESYNC (500*1024) +#define NSV_MAX_RESYNC_TRIES 300 + +/* + * References: + * (1) http://www.multimedia.cx/nsv-format.txt + * seems someone came to the same conclusions as me, and updated it: + * (2) http://www.stud.ktu.lt/~vitslav/nsv/nsv-format.txt + * http://www.stud.ktu.lt/~vitslav/nsv/ + * official docs + * (3) http://ultravox.aol.com/NSVFormat.rtf + * Sample files: + * (S1) http://www.nullsoft.com/nsv/samples/ + * http://www.nullsoft.com/nsv/samples/faster.nsv + * http://streamripper.sourceforge.net/openbb/read.php?TID=492&page=4 + */ + +/* + * notes on the header (Francois Revol): + * + * It is followed by strings, then a table, but nothing tells + * where the table begins according to (1). After checking faster.nsv, + * I believe NVSf[16-19] gives the size of the strings data + * (that is the offset of the data table after the header). + * After checking all samples from (S1) all confirms this. + * + * Then, about NSVf[12-15], faster.nsf has 179700. When veiwing it in VLC, + * I noticed there was about 1 NVSs chunk/s, so I ran + * strings faster.nsv | grep NSVs | wc -l + * which gave me 180. That leads me to think that NSVf[12-15] might be the + * file length in milliseconds. + * Let's try that: + * for f in *.nsv; do HTIME="$(od -t x4 "$f" | head -1 | sed 's/.* //')"; echo "'$f' $((0x$HTIME))s = $((0x$HTIME/1000/60)):$((0x$HTIME/1000%60))"; done + * except for nstrailer (which doesn't have an NSVf header), it repports correct time. + * + * nsvtrailer.nsv (S1) does not have any NSVf header, only NSVs chunks, + * so the header seems to not be mandatory. (for streaming). + * + * index slice duration check (excepts nsvtrailer.nsv): + * for f in [^n]*.nsv; do DUR="$(ffmpeg -i "$f" 2>/dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"; IC="$(ffmpeg -i "$f" 2>/dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"; echo "duration $DUR, slite time $(($DUR/$IC))"; done + */ + +/* + * TODO: + * - handle timestamps !!! + * - use index + * - mime-type in probe() + * - seek + */ + +#if 0 +struct NSVf_header { + uint32_t chunk_tag; /* 'NSVf' */ + uint32_t chunk_size; + uint32_t file_size; /* max 4GB ??? no one learns anything it seems :^) */ + uint32_t file_length; //unknown1; /* what about MSB of file_size ? */ + uint32_t info_strings_size; /* size of the info strings */ //unknown2; + uint32_t table_entries; + uint32_t table_entries_used; /* the left ones should be -1 */ +}; + +struct NSVs_header { + uint32_t chunk_tag; /* 'NSVs' */ + uint32_t v4cc; /* or 'NONE' */ + uint32_t a4cc; /* or 'NONE' */ + uint16_t vwidth; /* av_assert(vwidth%16==0) */ + uint16_t vheight; /* av_assert(vheight%16==0) */ + uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */ + uint16_t unknown; +}; + +struct nsv_avchunk_header { + uint8_t vchunk_size_lsb; + uint16_t vchunk_size_msb; /* value = (vchunk_size_msb << 4) | (vchunk_size_lsb >> 4) */ + uint16_t achunk_size; +}; + +struct nsv_pcm_header { + uint8_t bits_per_sample; + uint8_t channel_count; + uint16_t sample_rate; +}; +#endif + +/* variation from avi.h */ +/*typedef struct CodecTag { + int id; + unsigned int tag; +} CodecTag;*/ + +/* tags */ + +#define T_NSVF MKTAG('N', 'S', 'V', 'f') /* file header */ +#define T_NSVS MKTAG('N', 'S', 'V', 's') /* chunk header */ +#define T_TOC2 MKTAG('T', 'O', 'C', '2') /* extra index marker */ +#define T_NONE MKTAG('N', 'O', 'N', 'E') /* null a/v 4CC */ +#define T_SUBT MKTAG('S', 'U', 'B', 'T') /* subtitle aux data */ +#define T_ASYN MKTAG('A', 'S', 'Y', 'N') /* async a/v aux marker */ +#define T_KEYF MKTAG('K', 'E', 'Y', 'F') /* video keyframe aux marker (addition) */ + +#define TB_NSVF MKBETAG('N', 'S', 'V', 'f') +#define TB_NSVS MKBETAG('N', 'S', 'V', 's') + +/* hardcoded stream indexes */ +#define NSV_ST_VIDEO 0 +#define NSV_ST_AUDIO 1 +#define NSV_ST_SUBT 2 + +enum NSVStatus { + NSV_UNSYNC, + NSV_FOUND_NSVF, + NSV_HAS_READ_NSVF, + NSV_FOUND_NSVS, + NSV_HAS_READ_NSVS, + NSV_FOUND_BEEF, + NSV_GOT_VIDEO, + NSV_GOT_AUDIO, +}; + +typedef struct NSVStream { + int frame_offset; /* current frame (video) or byte (audio) counter + (used to compute the pts) */ + int scale; + int rate; + int sample_size; /* audio only data */ + int start; + + int new_frame_offset; /* temporary storage (used during seek) */ + int cum_len; /* temporary storage (used during seek) */ +} NSVStream; + +typedef struct { + int base_offset; + int NSVf_end; + uint32_t *nsvs_file_offset; + int index_entries; + enum NSVStatus state; + AVPacket ahead[2]; /* [v, a] if .data is !NULL there is something */ + /* cached */ + int64_t duration; + uint32_t vtag, atag; + uint16_t vwidth, vheight; + int16_t avsync; + AVRational framerate; + uint32_t *nsvs_timestamps; + //DVDemuxContext* dv_demux; +} NSVContext; + +static const AVCodecTag nsv_codec_video_tags[] = { + { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', ' ') }, + { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') }, + { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, + { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', ' ') }, + { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') }, + { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', ' ') }, + { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') }, + { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') }, + { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') }, + { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') }, +/* + { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') }, + { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') }, +*/ + { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, /* cf sample xvid decoder from nsv_codec_sdk.zip */ + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '3') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +static const AVCodecTag nsv_codec_audio_tags[] = { + { AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, + { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') }, + { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') }, + { AV_CODEC_ID_AAC, MKTAG('V', 'L', 'B', ' ') }, + { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', ' ') }, + { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +//static int nsv_load_index(AVFormatContext *s); +static int nsv_read_chunk(AVFormatContext *s, int fill_header); + +#define print_tag(str, tag, size) \ + av_dlog(NULL, "%s: tag=%c%c%c%c\n", \ + str, tag & 0xff, \ + (tag >> 8) & 0xff, \ + (tag >> 16) & 0xff, \ + (tag >> 24) & 0xff); + +/* try to find something we recognize, and set the state accordingly */ +static int nsv_resync(AVFormatContext *s) +{ + NSVContext *nsv = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t v = 0; + int i; + + av_dlog(s, "%s(), offset = %"PRId64", state = %d\n", __FUNCTION__, avio_tell(pb), nsv->state); + + //nsv->state = NSV_UNSYNC; + + for (i = 0; i < NSV_MAX_RESYNC; i++) { + if (url_feof(pb)) { + av_dlog(s, "NSV EOF\n"); + nsv->state = NSV_UNSYNC; + return -1; + } + v <<= 8; + v |= avio_r8(pb); + if (i < 8) { + av_dlog(s, "NSV resync: [%d] = %02x\n", i, v & 0x0FF); + } + + if ((v & 0x0000ffff) == 0xefbe) { /* BEEF */ + av_dlog(s, "NSV resynced on BEEF after %d bytes\n", i+1); + nsv->state = NSV_FOUND_BEEF; + return 0; + } + /* we read as big-endian, thus the MK*BE* */ + if (v == TB_NSVF) { /* NSVf */ + av_dlog(s, "NSV resynced on NSVf after %d bytes\n", i+1); + nsv->state = NSV_FOUND_NSVF; + return 0; + } + if (v == MKBETAG('N', 'S', 'V', 's')) { /* NSVs */ + av_dlog(s, "NSV resynced on NSVs after %d bytes\n", i+1); + nsv->state = NSV_FOUND_NSVS; + return 0; + } + + } + av_dlog(s, "NSV sync lost\n"); + return -1; +} + +static int nsv_parse_NSVf_header(AVFormatContext *s) +{ + NSVContext *nsv = s->priv_data; + AVIOContext *pb = s->pb; + unsigned int av_unused file_size; + unsigned int size; + int64_t duration; + int strings_size; + int table_entries; + int table_entries_used; + + av_dlog(s, "%s()\n", __FUNCTION__); + + nsv->state = NSV_UNSYNC; /* in case we fail */ + + size = avio_rl32(pb); + if (size < 28) + return -1; + nsv->NSVf_end = size; + + //s->file_size = (uint32_t)avio_rl32(pb); + file_size = (uint32_t)avio_rl32(pb); + av_dlog(s, "NSV NSVf chunk_size %u\n", size); + av_dlog(s, "NSV NSVf file_size %u\n", file_size); + + nsv->duration = duration = avio_rl32(pb); /* in ms */ + av_dlog(s, "NSV NSVf duration %"PRId64" ms\n", duration); + // XXX: store it in AVStreams + + strings_size = avio_rl32(pb); + table_entries = avio_rl32(pb); + table_entries_used = avio_rl32(pb); + av_dlog(s, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", + strings_size, table_entries, table_entries_used); + if (url_feof(pb)) + return -1; + + av_dlog(s, "NSV got header; filepos %"PRId64"\n", avio_tell(pb)); + + if (strings_size > 0) { + char *strings; /* last byte will be '\0' to play safe with str*() */ + char *p, *endp; + char *token, *value; + char quote; + + p = strings = av_mallocz((size_t)strings_size + 1); + if (!p) + return AVERROR(ENOMEM); + endp = strings + strings_size; + avio_read(pb, strings, strings_size); + while (p < endp) { + while (*p == ' ') + p++; /* strip out spaces */ + if (p >= endp-2) + break; + token = p; + p = strchr(p, '='); + if (!p || p >= endp-2) + break; + *p++ = '\0'; + quote = *p++; + value = p; + p = strchr(p, quote); + if (!p || p >= endp) + break; + *p++ = '\0'; + av_dlog(s, "NSV NSVf INFO: %s='%s'\n", token, value); + av_dict_set(&s->metadata, token, value, 0); + } + av_free(strings); + } + if (url_feof(pb)) + return -1; + + av_dlog(s, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb)); + + if (table_entries_used > 0) { + int i; + nsv->index_entries = table_entries_used; + if((unsigned)table_entries_used >= UINT_MAX / sizeof(uint32_t)) + return -1; + nsv->nsvs_file_offset = av_malloc((unsigned)table_entries_used * sizeof(uint32_t)); + if (!nsv->nsvs_file_offset) + return AVERROR(ENOMEM); + + for(i=0;insvs_file_offset[i] = avio_rl32(pb) + size; + + if(table_entries > table_entries_used && + avio_rl32(pb) == MKTAG('T','O','C','2')) { + nsv->nsvs_timestamps = av_malloc((unsigned)table_entries_used*sizeof(uint32_t)); + if (!nsv->nsvs_timestamps) + return AVERROR(ENOMEM); + for(i=0;insvs_timestamps[i] = avio_rl32(pb); + } + } + } + + av_dlog(s, "NSV got index; filepos %"PRId64"\n", avio_tell(pb)); + +#ifdef DEBUG_DUMP_INDEX +#define V(v) ((v<0x20 || v > 127)?'.':v) + /* dump index */ + av_dlog(s, "NSV %d INDEX ENTRIES:\n", table_entries); + av_dlog(s, "NSV [dataoffset][fileoffset]\n", table_entries); + for (i = 0; i < table_entries; i++) { + unsigned char b[8]; + avio_seek(pb, size + nsv->nsvs_file_offset[i], SEEK_SET); + avio_read(pb, b, 8); + av_dlog(s, "NSV [0x%08lx][0x%08lx]: %02x %02x %02x %02x %02x %02x %02x %02x" + "%c%c%c%c%c%c%c%c\n", + nsv->nsvs_file_offset[i], size + nsv->nsvs_file_offset[i], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + V(b[0]), V(b[1]), V(b[2]), V(b[3]), V(b[4]), V(b[5]), V(b[6]), V(b[7]) ); + } + //avio_seek(pb, size, SEEK_SET); /* go back to end of header */ +#undef V +#endif + + avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ + + if (url_feof(pb)) + return -1; + nsv->state = NSV_HAS_READ_NSVF; + return 0; +} + +static int nsv_parse_NSVs_header(AVFormatContext *s) +{ + NSVContext *nsv = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t vtag, atag; + uint16_t vwidth, vheight; + AVRational framerate; + int i; + AVStream *st; + NSVStream *nst; + av_dlog(s, "%s()\n", __FUNCTION__); + + vtag = avio_rl32(pb); + atag = avio_rl32(pb); + vwidth = avio_rl16(pb); + vheight = avio_rl16(pb); + i = avio_r8(pb); + + av_dlog(s, "NSV NSVs framerate code %2x\n", i); + if(i&0x80) { /* odd way of giving native framerates from docs */ + int t=(i & 0x7F)>>2; + if(t<16) framerate = (AVRational){1, t+1}; + else framerate = (AVRational){t-15, 1}; + + if(i&1){ + framerate.num *= 1000; + framerate.den *= 1001; + } + + if((i&3)==3) framerate.num *= 24; + else if((i&3)==2) framerate.num *= 25; + else framerate.num *= 30; + } + else + framerate= (AVRational){i, 1}; + + nsv->avsync = avio_rl16(pb); + nsv->framerate = framerate; + + print_tag("NSV NSVs vtag", vtag, 0); + print_tag("NSV NSVs atag", atag, 0); + av_dlog(s, "NSV NSVs vsize %dx%d\n", vwidth, vheight); + + /* XXX change to ap != NULL ? */ + if (s->nb_streams == 0) { /* streams not yet published, let's do that */ + nsv->vtag = vtag; + nsv->atag = atag; + nsv->vwidth = vwidth; + nsv->vheight = vwidth; + if (vtag != T_NONE) { + int i; + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + + st->id = NSV_ST_VIDEO; + nst = av_mallocz(sizeof(NSVStream)); + if (!nst) + goto fail; + st->priv_data = nst; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_tag = vtag; + st->codec->codec_id = ff_codec_get_id(nsv_codec_video_tags, vtag); + st->codec->width = vwidth; + st->codec->height = vheight; + st->codec->bits_per_coded_sample = 24; /* depth XXX */ + + avpriv_set_pts_info(st, 64, framerate.den, framerate.num); + st->start_time = 0; + st->duration = av_rescale(nsv->duration, framerate.num, 1000*framerate.den); + + for(i=0;iindex_entries;i++) { + if(nsv->nsvs_timestamps) { + av_add_index_entry(st, nsv->nsvs_file_offset[i], nsv->nsvs_timestamps[i], + 0, 0, AVINDEX_KEYFRAME); + } else { + int64_t ts = av_rescale(i*nsv->duration/nsv->index_entries, framerate.num, 1000*framerate.den); + av_add_index_entry(st, nsv->nsvs_file_offset[i], ts, 0, 0, AVINDEX_KEYFRAME); + } + } + } + if (atag != T_NONE) { +#ifndef DISABLE_AUDIO + st = avformat_new_stream(s, NULL); + if (!st) + goto fail; + + st->id = NSV_ST_AUDIO; + nst = av_mallocz(sizeof(NSVStream)); + if (!nst) + goto fail; + st->priv_data = nst; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = atag; + st->codec->codec_id = ff_codec_get_id(nsv_codec_audio_tags, atag); + + st->need_parsing = AVSTREAM_PARSE_FULL; /* for PCM we will read a chunk later and put correct info */ + + /* set timebase to common denominator of ms and framerate */ + avpriv_set_pts_info(st, 64, 1, framerate.num*1000); + st->start_time = 0; + st->duration = (int64_t)nsv->duration * framerate.num; +#endif + } +#ifdef CHECK_SUBSEQUENT_NSVS + } else { + if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) { + av_dlog(s, "NSV NSVs header values differ from the first one!!!\n"); + //return -1; + } +#endif /* CHECK_SUBSEQUENT_NSVS */ + } + + nsv->state = NSV_HAS_READ_NSVS; + return 0; +fail: + /* XXX */ + nsv->state = NSV_UNSYNC; + return -1; +} + +static int nsv_read_header(AVFormatContext *s) +{ + NSVContext *nsv = s->priv_data; + int i, err; + + av_dlog(s, "%s()\n", __FUNCTION__); + av_dlog(s, "filename '%s'\n", s->filename); + + nsv->state = NSV_UNSYNC; + nsv->ahead[0].data = nsv->ahead[1].data = NULL; + + for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) { + if (nsv_resync(s) < 0) + return -1; + if (nsv->state == NSV_FOUND_NSVF) { + err = nsv_parse_NSVf_header(s); + if (err < 0) + return err; + } + /* we need the first NSVs also... */ + if (nsv->state == NSV_FOUND_NSVS) { + err = nsv_parse_NSVs_header(s); + if (err < 0) + return err; + break; /* we just want the first one */ + } + } + if (s->nb_streams < 1) /* no luck so far */ + return -1; + /* now read the first chunk, so we can attempt to decode more info */ + err = nsv_read_chunk(s, 1); + + av_dlog(s, "parsed header\n"); + return err; +} + +static int nsv_read_chunk(AVFormatContext *s, int fill_header) +{ + NSVContext *nsv = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st[2] = {NULL, NULL}; + NSVStream *nst; + AVPacket *pkt; + int i, err = 0; + uint8_t auxcount; /* number of aux metadata, also 4 bits of vsize */ + uint32_t vsize; + uint16_t asize; + uint16_t auxsize; + + av_dlog(s, "%s(%d)\n", __FUNCTION__, fill_header); + + if (nsv->ahead[0].data || nsv->ahead[1].data) + return 0; //-1; /* hey! eat what you've in your plate first! */ + +null_chunk_retry: + if (url_feof(pb)) + return -1; + + for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) + err = nsv_resync(s); + if (err < 0) + return err; + if (nsv->state == NSV_FOUND_NSVS) + err = nsv_parse_NSVs_header(s); + if (err < 0) + return err; + if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF) + return -1; + + auxcount = avio_r8(pb); + vsize = avio_rl16(pb); + asize = avio_rl16(pb); + vsize = (vsize << 4) | (auxcount >> 4); + auxcount &= 0x0f; + av_dlog(s, "NSV CHUNK %d aux, %u bytes video, %d bytes audio\n", auxcount, vsize, asize); + /* skip aux stuff */ + for (i = 0; i < auxcount; i++) { + uint32_t av_unused auxtag; + auxsize = avio_rl16(pb); + auxtag = avio_rl32(pb); + av_dlog(s, "NSV aux data: '%c%c%c%c', %d bytes\n", + (auxtag & 0x0ff), + ((auxtag >> 8) & 0x0ff), + ((auxtag >> 16) & 0x0ff), + ((auxtag >> 24) & 0x0ff), + auxsize); + avio_skip(pb, auxsize); + vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */ + } + + if (url_feof(pb)) + return -1; + if (!vsize && !asize) { + nsv->state = NSV_UNSYNC; + goto null_chunk_retry; + } + + /* map back streams to v,a */ + if (s->nb_streams > 0) + st[s->streams[0]->id] = s->streams[0]; + if (s->nb_streams > 1) + st[s->streams[1]->id] = s->streams[1]; + + if (vsize && st[NSV_ST_VIDEO]) { + nst = st[NSV_ST_VIDEO]->priv_data; + pkt = &nsv->ahead[NSV_ST_VIDEO]; + av_get_packet(pb, pkt, vsize); + pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO; + pkt->dts = nst->frame_offset; + pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ + for (i = 0; i < FFMIN(8, vsize); i++) + av_dlog(s, "NSV video: [%d] = %02x\n", i, pkt->data[i]); + } + if(st[NSV_ST_VIDEO]) + ((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset++; + + if (asize && st[NSV_ST_AUDIO]) { + nst = st[NSV_ST_AUDIO]->priv_data; + pkt = &nsv->ahead[NSV_ST_AUDIO]; + /* read raw audio specific header on the first audio chunk... */ + /* on ALL audio chunks ?? seems so! */ + if (asize && st[NSV_ST_AUDIO]->codec->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { + uint8_t bps; + uint8_t channels; + uint16_t samplerate; + bps = avio_r8(pb); + channels = avio_r8(pb); + samplerate = avio_rl16(pb); + asize-=4; + av_dlog(s, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate); + if (fill_header) { + st[NSV_ST_AUDIO]->need_parsing = AVSTREAM_PARSE_NONE; /* we know everything */ + if (bps != 16) { + av_dlog(s, "NSV AUDIO bit/sample != 16 (%d)!!!\n", bps); + } + if(channels) + bps /= channels; // ??? + else + av_log(s, AV_LOG_WARNING, "Channels is 0\n"); + if (bps == 8) + st[NSV_ST_AUDIO]->codec->codec_id = AV_CODEC_ID_PCM_U8; + samplerate /= 4;/* UGH ??? XXX */ + channels = 1; + st[NSV_ST_AUDIO]->codec->channels = channels; + st[NSV_ST_AUDIO]->codec->sample_rate = samplerate; + av_dlog(s, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate); + } + } + av_get_packet(pb, pkt, asize); + pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO; + pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ + if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) { + /* on a nsvs frame we have new information on a/v sync */ + pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1); + pkt->dts *= (int64_t)1000 * nsv->framerate.den; + pkt->dts += (int64_t)nsv->avsync * nsv->framerate.num; + av_dlog(s, "NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts); + } + nst->frame_offset++; + } + + nsv->state = NSV_UNSYNC; + return 0; +} + + +static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + NSVContext *nsv = s->priv_data; + int i, err = 0; + + av_dlog(s, "%s()\n", __FUNCTION__); + + /* in case we don't already have something to eat ... */ + if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL) + err = nsv_read_chunk(s, 0); + if (err < 0) + return err; + + /* now pick one of the plates */ + for (i = 0; i < 2; i++) { + if (nsv->ahead[i].data) { + av_dlog(s, "%s: using cached packet[%d]\n", __FUNCTION__, i); + /* avoid the cost of new_packet + memcpy(->data) */ + memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket)); + nsv->ahead[i].data = NULL; /* we ate that one */ + return pkt->size; + } + } + + /* this restaurant is not approvisionned :^] */ + return -1; +} + +static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + NSVContext *nsv = s->priv_data; + AVStream *st = s->streams[stream_index]; + NSVStream *nst = st->priv_data; + int index; + + index = av_index_search_timestamp(st, timestamp, flags); + if(index < 0) + return -1; + + if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) + return -1; + + nst->frame_offset = st->index_entries[index].timestamp; + nsv->state = NSV_UNSYNC; + return 0; +} + +static int nsv_read_close(AVFormatContext *s) +{ +/* int i; */ + NSVContext *nsv = s->priv_data; + + av_freep(&nsv->nsvs_file_offset); + av_freep(&nsv->nsvs_timestamps); + if (nsv->ahead[0].data) + av_free_packet(&nsv->ahead[0]); + if (nsv->ahead[1].data) + av_free_packet(&nsv->ahead[1]); + +#if 0 + + for(i=0;inb_streams;i++) { + AVStream *st = s->streams[i]; + NSVStream *ast = st->priv_data; + if(ast){ + av_free(ast->index_entries); + av_free(ast); + } + av_free(st->codec->palctrl); + } + +#endif + return 0; +} + +static int nsv_probe(AVProbeData *p) +{ + int i, score = 0; + + av_dlog(NULL, "nsv_probe(), buf_size %d\n", p->buf_size); + /* check file header */ + /* streamed files might not have any header */ + if (p->buf[0] == 'N' && p->buf[1] == 'S' && + p->buf[2] == 'V' && (p->buf[3] == 'f' || p->buf[3] == 's')) + return AVPROBE_SCORE_MAX; + /* XXX: do streamed files always start at chunk boundary ?? */ + /* or do we need to search NSVs in the byte stream ? */ + /* seems the servers don't bother starting clean chunks... */ + /* sometimes even the first header is at 9KB or something :^) */ + for (i = 1; i < p->buf_size - 3; i++) { + if (AV_RL32(p->buf + i) == AV_RL32("NSVs")) { + /* Get the chunk size and check if at the end we are getting 0xBEEF */ + int vsize = AV_RL24(p->buf+i+19) >> 4; + int asize = AV_RL16(p->buf+i+22); + int offset = i + 23 + asize + vsize + 1; + if (offset <= p->buf_size - 2 && AV_RL16(p->buf + offset) == 0xBEEF) + return 4*AVPROBE_SCORE_MAX/5; + score = AVPROBE_SCORE_MAX/5; + } + } + /* so we'll have more luck on extension... */ + if (av_match_ext(p->filename, "nsv")) + return AVPROBE_SCORE_MAX/2; + /* FIXME: add mime-type check */ + return score; +} + +AVInputFormat ff_nsv_demuxer = { + .name = "nsv", + .long_name = NULL_IF_CONFIG_SMALL("Nullsoft Streaming Video"), + .priv_data_size = sizeof(NSVContext), + .read_probe = nsv_probe, + .read_header = nsv_read_header, + .read_packet = nsv_read_packet, + .read_close = nsv_read_close, + .read_seek = nsv_read_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nsvdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nsvdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/nsvdec.o libavformat/nsvdec.o: libavformat/nsvdec.c \ + libavutil/attributes.h libavutil/mathematics.h libavutil/attributes.h \ + libavutil/rational.h libavutil/intfloat.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/avconfig.h config.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nsvdec.o Binary file ffmpeg/libavformat/nsvdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nullenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nullenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,36 @@ +/* + * RAW null muxer + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" + +static int null_write_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + return 0; +} + +AVOutputFormat ff_null_muxer = { + .name = "null", + .long_name = NULL_IF_CONFIG_SMALL("raw null video"), + .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_packet = null_write_packet, + .flags = AVFMT_NOFILE | AVFMT_NOTIMESTAMPS | AVFMT_RAWPICTURE, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nullenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nullenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/nullenc.o libavformat/nullenc.o: libavformat/nullenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nullenc.o Binary file ffmpeg/libavformat/nullenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nut.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nut.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,264 @@ +/* + * nut + * Copyright (c) 2004-2007 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mathematics.h" +#include "libavutil/tree.h" +#include "nut.h" +#include "riff.h" +#include "internal.h" + +const AVCodecTag ff_nut_subtitle_tags[] = { + { AV_CODEC_ID_TEXT , MKTAG('U', 'T', 'F', '8') }, + { AV_CODEC_ID_SSA , MKTAG('S', 'S', 'A', 0 ) }, + { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('D', 'V', 'D', 'S') }, + { AV_CODEC_ID_DVB_SUBTITLE, MKTAG('D', 'V', 'B', 'S') }, + { AV_CODEC_ID_DVB_TELETEXT, MKTAG('D', 'V', 'B', 'T') }, + { AV_CODEC_ID_NONE , 0 } +}; + +const AVCodecTag ff_nut_data_tags[] = { + { AV_CODEC_ID_TEXT , MKTAG('U', 'T', 'F', '8') }, + { AV_CODEC_ID_NONE , 0 } +}; + +const AVCodecTag ff_nut_video_tags[] = { + { AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 15 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 15 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(15 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 0 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 0 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('A', 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 0 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 24 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 24 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '1', '1', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '2', '2', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '0', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '0', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '4', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('4', '4', '4', 'P') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '1', 'W', '0') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '0', 'W', '1') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 4 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 4 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', '4', 'B', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', '4', 'B', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'G', 'R', 48 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', 48 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'B', 'G', 'R') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(48 , 'R', 'G', 'B') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('B', 'R', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'B', 'A', 64 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'B', 'R', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(64 , 'R', 'B', 'A') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 12 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '1', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 11 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 11 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 10 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 10 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '3', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '3', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 8 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '2', 0 , 8 ) }, + + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 9 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 0 , '1', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 9 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 11 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 9 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 10 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 9 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG( 9 , 0 , '4', 'Y') }, + + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '1', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 11 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 10 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 10 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 0 , '4', 'Y') }, + + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '1', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '1', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 11 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 11 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 10 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 10 , '4', 'Y') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('Y', '4', 0 , 16 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 0 , '4', 'Y') }, + + { AV_CODEC_ID_NONE , 0 } +}; + +static const AVCodecTag nut_audio_extra_tags[] = { + { AV_CODEC_ID_PCM_ALAW, MKTAG('A', 'L', 'A', 'W') }, + { AV_CODEC_ID_PCM_MULAW, MKTAG('U', 'L', 'A', 'W') }, + { AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, + { AV_CODEC_ID_NONE, 0 } +}; + +const AVCodecTag ff_nut_audio_tags[] = { + { AV_CODEC_ID_PCM_F32BE, MKTAG(32 , 'D', 'F', 'P') }, + { AV_CODEC_ID_PCM_F32LE, MKTAG('P', 'F', 'D', 32 ) }, + { AV_CODEC_ID_PCM_F64BE, MKTAG(64 , 'D', 'F', 'P') }, + { AV_CODEC_ID_PCM_F64LE, MKTAG('P', 'F', 'D', 64 ) }, + { AV_CODEC_ID_PCM_S16BE, MKTAG(16 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('P', 'S', 'D', 16 ) }, + { AV_CODEC_ID_PCM_S24BE, MKTAG(24 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24 ) }, + { AV_CODEC_ID_PCM_S32BE, MKTAG(32 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32 ) }, + { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8 ) }, + { AV_CODEC_ID_PCM_U16BE, MKTAG(16 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16 ) }, + { AV_CODEC_ID_PCM_U24BE, MKTAG(24 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U24LE, MKTAG('P', 'U', 'D', 24 ) }, + { AV_CODEC_ID_PCM_U32BE, MKTAG(32 , 'D', 'U', 'P') }, + { AV_CODEC_ID_PCM_U32LE, MKTAG('P', 'U', 'D', 32 ) }, + { AV_CODEC_ID_PCM_U8, MKTAG('P', 'U', 'D', 8 ) }, + { AV_CODEC_ID_PCM_S8_PLANAR, MKTAG('P', 'S', 'P', 8 ) }, + { AV_CODEC_ID_PCM_S16BE_PLANAR, MKTAG(16 , 'P', 'S', 'P') }, + { AV_CODEC_ID_PCM_S16LE_PLANAR, MKTAG('P', 'S', 'P', 16 ) }, + { AV_CODEC_ID_PCM_S24LE_PLANAR, MKTAG('P', 'S', 'P', 24 ) }, + { AV_CODEC_ID_PCM_S32LE_PLANAR, MKTAG('P', 'S', 'P', 32 ) }, + { AV_CODEC_ID_NONE, 0 } +}; + +const AVCodecTag * const ff_nut_codec_tags[] = { + ff_nut_video_tags, ff_nut_audio_tags, ff_nut_subtitle_tags, + ff_codec_bmp_tags, ff_codec_wav_tags, nut_audio_extra_tags, ff_nut_data_tags, 0 +}; + +void ff_nut_reset_ts(NUTContext *nut, AVRational time_base, int64_t val){ + int i; + for(i=0; iavf->nb_streams; i++){ + nut->stream[i].last_pts= av_rescale_rnd( + val, + time_base.num * (int64_t)nut->stream[i].time_base->den, + time_base.den * (int64_t)nut->stream[i].time_base->num, + AV_ROUND_DOWN); + } +} + +int64_t ff_lsb2full(StreamContext *stream, int64_t lsb){ + int64_t mask = (1ULL<msb_pts_shift)-1; + int64_t delta= stream->last_pts - mask/2; + return ((lsb - delta)&mask) + delta; +} + +int ff_nut_sp_pos_cmp(const Syncpoint *a, const Syncpoint *b){ + return ((a->pos - b->pos) >> 32) - ((b->pos - a->pos) >> 32); +} + +int ff_nut_sp_pts_cmp(const Syncpoint *a, const Syncpoint *b){ + return ((a->ts - b->ts) >> 32) - ((b->ts - a->ts) >> 32); +} + +void ff_nut_add_sp(NUTContext *nut, int64_t pos, int64_t back_ptr, int64_t ts){ + Syncpoint *sp= av_mallocz(sizeof(Syncpoint)); + struct AVTreeNode *node = av_tree_node_alloc(); + + nut->sp_count++; + + sp->pos= pos; + sp->back_ptr= back_ptr; + sp->ts= ts; + av_tree_insert(&nut->syncpoints, sp, (void *) ff_nut_sp_pos_cmp, &node); + if(node){ + av_free(sp); + av_free(node); + } +} + +static int enu_free(void *opaque, void *elem) +{ + av_free(elem); + return 0; +} + +void ff_nut_free_sp(NUTContext *nut) +{ + av_tree_enumerate(nut->syncpoints, NULL, NULL, enu_free); + av_tree_destroy(nut->syncpoints); +} + +const Dispositions ff_nut_dispositions[] = { + {"default" , AV_DISPOSITION_DEFAULT}, + {"dub" , AV_DISPOSITION_DUB}, + {"original" , AV_DISPOSITION_ORIGINAL}, + {"comment" , AV_DISPOSITION_COMMENT}, + {"lyrics" , AV_DISPOSITION_LYRICS}, + {"karaoke" , AV_DISPOSITION_KARAOKE}, + {"" , 0} +}; + +const AVMetadataConv ff_nut_metadata_conv[] = { + { "Author", "artist" }, + { "X-CreationTime", "date" }, + { "CreationTime", "date" }, + { "SourceFilename", "filename" }, + { "X-Language", "language" }, + { "X-Disposition", "disposition" }, + { "X-Replaces", "replaces" }, + { "X-Depends", "depends" }, + { "X-Uses", "uses" }, + { "X-UsesFont", "usesfont" }, + { 0 }, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nut.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nut.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/nut.o libavformat/nut.o: libavformat/nut.c \ + libavutil/mathematics.h libavutil/attributes.h libavutil/rational.h \ + libavutil/intfloat.h libavutil/tree.h libavutil/version.h \ + libavformat/nut.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/mathematics.h libavutil/intfloat_readwrite.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/version.h \ + libavutil/old_pix_fmts.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/dict.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h libavformat/metadata.h libavformat/riff.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nut.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nut.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,133 @@ +/* + * "NUT" Container Format (de)muxer + * Copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_NUT_H +#define AVFORMAT_NUT_H + +//#include +//#include "libavutil/adler32.h" +//#include "libavcodec/mpegaudio.h" +#include "avformat.h" +#include "internal.h" +#include "metadata.h" + +#define MAIN_STARTCODE (0x7A561F5F04ADULL + (((uint64_t)('N'<<8) + 'M')<<48)) +#define STREAM_STARTCODE (0x11405BF2F9DBULL + (((uint64_t)('N'<<8) + 'S')<<48)) +#define SYNCPOINT_STARTCODE (0xE4ADEECA4569ULL + (((uint64_t)('N'<<8) + 'K')<<48)) +#define INDEX_STARTCODE (0xDD672F23E64EULL + (((uint64_t)('N'<<8) + 'X')<<48)) +#define INFO_STARTCODE (0xAB68B596BA78ULL + (((uint64_t)('N'<<8) + 'I')<<48)) + +#define ID_STRING "nut/multimedia container\0" + +#define MAX_DISTANCE (1024*32-1) + +typedef enum{ + FLAG_KEY = 1, /// maxlen) { + avio_r8(bc); + len--; + } + + if (maxlen) + string[FFMIN(len, maxlen - 1)] = 0; + + if (maxlen == len) + return -1; + else + return 0; +} + +static int64_t get_s(AVIOContext *bc) +{ + int64_t v = ffio_read_varlen(bc) + 1; + + if (v & 1) + return -(v >> 1); + else + return (v >> 1); +} + +static uint64_t get_fourcc(AVIOContext *bc) +{ + unsigned int len = ffio_read_varlen(bc); + + if (len == 2) + return avio_rl16(bc); + else if (len == 4) + return avio_rl32(bc); + else { + av_log(NULL, AV_LOG_ERROR, "Unsupported fourcc length %d\n", len); + return -1; + } +} + +#ifdef TRACE +static inline uint64_t get_v_trace(AVIOContext *bc, const char *file, + const char *func, int line) +{ + uint64_t v = ffio_read_varlen(bc); + + av_log(NULL, AV_LOG_DEBUG, "get_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", + v, v, file, func, line); + return v; +} + +static inline int64_t get_s_trace(AVIOContext *bc, const char *file, + const char *func, int line) +{ + int64_t v = get_s(bc); + + av_log(NULL, AV_LOG_DEBUG, "get_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", + v, v, file, func, line); + return v; +} + +static inline uint64_t get_4cc_trace(AVIOContext *bc, char *file, + char *func, int line) +{ + uint64_t v = get_fourcc(bc); + + av_log(NULL, AV_LOG_DEBUG, "get_fourcc %5"PRId64" / %"PRIX64" in %s %s:%d\n", + v, v, file, func, line); + return v; +} +#define ffio_read_varlen(bc) get_v_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#define get_s(bc) get_s_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#define get_fourcc(bc) get_4cc_trace(bc, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#endif + +static int get_packetheader(NUTContext *nut, AVIOContext *bc, + int calculate_checksum, uint64_t startcode) +{ + int64_t size; +// start = avio_tell(bc) - 8; + + startcode = av_be2ne64(startcode); + startcode = ff_crc04C11DB7_update(0, (uint8_t*) &startcode, 8); + + ffio_init_checksum(bc, ff_crc04C11DB7_update, startcode); + size = ffio_read_varlen(bc); + if (size > 4096) + avio_rb32(bc); + if (ffio_get_checksum(bc) && size > 4096) + return -1; + + ffio_init_checksum(bc, calculate_checksum ? ff_crc04C11DB7_update : NULL, 0); + + return size; +} + +static uint64_t find_any_startcode(AVIOContext *bc, int64_t pos) +{ + uint64_t state = 0; + + if (pos >= 0) + /* Note, this may fail if the stream is not seekable, but that should + * not matter, as in this case we simply start where we currently are */ + avio_seek(bc, pos, SEEK_SET); + while (!url_feof(bc)) { + state = (state << 8) | avio_r8(bc); + if ((state >> 56) != 'N') + continue; + switch (state) { + case MAIN_STARTCODE: + case STREAM_STARTCODE: + case SYNCPOINT_STARTCODE: + case INFO_STARTCODE: + case INDEX_STARTCODE: + return state; + } + } + + return 0; +} + +/** + * Find the given startcode. + * @param code the startcode + * @param pos the start position of the search, or -1 if the current position + * @return the position of the startcode or -1 if not found + */ +static int64_t find_startcode(AVIOContext *bc, uint64_t code, int64_t pos) +{ + for (;;) { + uint64_t startcode = find_any_startcode(bc, pos); + if (startcode == code) + return avio_tell(bc) - 8; + else if (startcode == 0) + return -1; + pos = -1; + } +} + +static int nut_probe(AVProbeData *p) +{ + int i; + uint64_t code = 0; + + for (i = 0; i < p->buf_size; i++) { + code = (code << 8) | p->buf[i]; + if (code == MAIN_STARTCODE) + return AVPROBE_SCORE_MAX; + } + return 0; +} + +#define GET_V(dst, check) \ + do { \ + tmp = ffio_read_varlen(bc); \ + if (!(check)) { \ + av_log(s, AV_LOG_ERROR, "Error " #dst " is (%"PRId64")\n", tmp); \ + return -1; \ + } \ + dst = tmp; \ + } while (0) + +static int skip_reserved(AVIOContext *bc, int64_t pos) +{ + pos -= avio_tell(bc); + if (pos < 0) { + avio_seek(bc, pos, SEEK_CUR); + return -1; + } else { + while (pos--) + avio_r8(bc); + return 0; + } +} + +static int decode_main_header(NUTContext *nut) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + uint64_t tmp, end; + unsigned int stream_count; + int i, j, count; + int tmp_stream, tmp_mul, tmp_pts, tmp_size, tmp_res, tmp_head_idx; + + end = get_packetheader(nut, bc, 1, MAIN_STARTCODE); + end += avio_tell(bc); + + GET_V(tmp, tmp >= 2 && tmp <= 3); + GET_V(stream_count, tmp > 0 && tmp <= NUT_MAX_STREAMS); + + nut->max_distance = ffio_read_varlen(bc); + if (nut->max_distance > 65536) { + av_log(s, AV_LOG_DEBUG, "max_distance %d\n", nut->max_distance); + nut->max_distance = 65536; + } + + GET_V(nut->time_base_count, tmp > 0 && tmp < INT_MAX / sizeof(AVRational)); + nut->time_base = av_malloc(nut->time_base_count * sizeof(AVRational)); + + for (i = 0; i < nut->time_base_count; i++) { + GET_V(nut->time_base[i].num, tmp > 0 && tmp < (1ULL << 31)); + GET_V(nut->time_base[i].den, tmp > 0 && tmp < (1ULL << 31)); + if (av_gcd(nut->time_base[i].num, nut->time_base[i].den) != 1) { + av_log(s, AV_LOG_ERROR, "time base invalid\n"); + return AVERROR_INVALIDDATA; + } + } + tmp_pts = 0; + tmp_mul = 1; + tmp_stream = 0; + tmp_head_idx = 0; + for (i = 0; i < 256;) { + int tmp_flags = ffio_read_varlen(bc); + int tmp_fields = ffio_read_varlen(bc); + + if (tmp_fields > 0) + tmp_pts = get_s(bc); + if (tmp_fields > 1) + tmp_mul = ffio_read_varlen(bc); + if (tmp_fields > 2) + tmp_stream = ffio_read_varlen(bc); + if (tmp_fields > 3) + tmp_size = ffio_read_varlen(bc); + else + tmp_size = 0; + if (tmp_fields > 4) + tmp_res = ffio_read_varlen(bc); + else + tmp_res = 0; + if (tmp_fields > 5) + count = ffio_read_varlen(bc); + else + count = tmp_mul - tmp_size; + if (tmp_fields > 6) + get_s(bc); + if (tmp_fields > 7) + tmp_head_idx = ffio_read_varlen(bc); + + while (tmp_fields-- > 8) + ffio_read_varlen(bc); + + if (count == 0 || i + count > 256) { + av_log(s, AV_LOG_ERROR, "illegal count %d at %d\n", count, i); + return AVERROR_INVALIDDATA; + } + if (tmp_stream >= stream_count) { + av_log(s, AV_LOG_ERROR, "illegal stream number\n"); + return AVERROR_INVALIDDATA; + } + + for (j = 0; j < count; j++, i++) { + if (i == 'N') { + nut->frame_code[i].flags = FLAG_INVALID; + j--; + continue; + } + nut->frame_code[i].flags = tmp_flags; + nut->frame_code[i].pts_delta = tmp_pts; + nut->frame_code[i].stream_id = tmp_stream; + nut->frame_code[i].size_mul = tmp_mul; + nut->frame_code[i].size_lsb = tmp_size + j; + nut->frame_code[i].reserved_count = tmp_res; + nut->frame_code[i].header_idx = tmp_head_idx; + } + } + av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); + + if (end > avio_tell(bc) + 4) { + int rem = 1024; + GET_V(nut->header_count, tmp < 128U); + nut->header_count++; + for (i = 1; i < nut->header_count; i++) { + uint8_t *hdr; + GET_V(nut->header_len[i], tmp > 0 && tmp < 256); + rem -= nut->header_len[i]; + if (rem < 0) { + av_log(s, AV_LOG_ERROR, "invalid elision header\n"); + return AVERROR_INVALIDDATA; + } + hdr = av_malloc(nut->header_len[i]); + if (!hdr) + return AVERROR(ENOMEM); + avio_read(bc, hdr, nut->header_len[i]); + nut->header[i] = hdr; + } + av_assert0(nut->header_len[0] == 0); + } + + if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n"); + return AVERROR_INVALIDDATA; + } + + nut->stream = av_mallocz(sizeof(StreamContext) * stream_count); + for (i = 0; i < stream_count; i++) + avformat_new_stream(s, NULL); + + return 0; +} + +static int decode_stream_header(NUTContext *nut) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + StreamContext *stc; + int class, stream_id; + uint64_t tmp, end; + AVStream *st; + + end = get_packetheader(nut, bc, 1, STREAM_STARTCODE); + end += avio_tell(bc); + + GET_V(stream_id, tmp < s->nb_streams && !nut->stream[tmp].time_base); + stc = &nut->stream[stream_id]; + st = s->streams[stream_id]; + if (!st) + return AVERROR(ENOMEM); + + class = ffio_read_varlen(bc); + tmp = get_fourcc(bc); + st->codec->codec_tag = tmp; + switch (class) { + case 0: + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { + ff_nut_video_tags, + ff_codec_bmp_tags, + 0 + }, + tmp); + break; + case 1: + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = av_codec_get_id((const AVCodecTag * const []) { + ff_nut_audio_tags, + ff_codec_wav_tags, + 0 + }, + tmp); + break; + case 2: + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = ff_codec_get_id(ff_nut_subtitle_tags, tmp); + break; + case 3: + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = ff_codec_get_id(ff_nut_data_tags, tmp); + break; + default: + av_log(s, AV_LOG_ERROR, "unknown stream class (%d)\n", class); + return -1; + } + if (class < 3 && st->codec->codec_id == AV_CODEC_ID_NONE) + av_log(s, AV_LOG_ERROR, + "Unknown codec tag '0x%04x' for stream number %d\n", + (unsigned int) tmp, stream_id); + + GET_V(stc->time_base_id, tmp < nut->time_base_count); + GET_V(stc->msb_pts_shift, tmp < 16); + stc->max_pts_distance = ffio_read_varlen(bc); + GET_V(stc->decode_delay, tmp < 1000); // sanity limit, raise this if Moore's law is true + st->codec->has_b_frames = stc->decode_delay; + ffio_read_varlen(bc); // stream flags + + GET_V(st->codec->extradata_size, tmp < (1 << 30)); + if (st->codec->extradata_size) { + st->codec->extradata = av_mallocz(st->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + avio_read(bc, st->codec->extradata, st->codec->extradata_size); + } + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + GET_V(st->codec->width, tmp > 0); + GET_V(st->codec->height, tmp > 0); + st->sample_aspect_ratio.num = ffio_read_varlen(bc); + st->sample_aspect_ratio.den = ffio_read_varlen(bc); + if ((!st->sample_aspect_ratio.num) != (!st->sample_aspect_ratio.den)) { + av_log(s, AV_LOG_ERROR, "invalid aspect ratio %d/%d\n", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); + return -1; + } + ffio_read_varlen(bc); /* csp type */ + } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + GET_V(st->codec->sample_rate, tmp > 0); + ffio_read_varlen(bc); // samplerate_den + GET_V(st->codec->channels, tmp > 0); + } + if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, + "stream header %d checksum mismatch\n", stream_id); + return -1; + } + stc->time_base = &nut->time_base[stc->time_base_id]; + avpriv_set_pts_info(s->streams[stream_id], 63, stc->time_base->num, + stc->time_base->den); + return 0; +} + +static void set_disposition_bits(AVFormatContext *avf, char *value, + int stream_id) +{ + int flag = 0, i; + + for (i = 0; ff_nut_dispositions[i].flag; ++i) + if (!strcmp(ff_nut_dispositions[i].str, value)) + flag = ff_nut_dispositions[i].flag; + if (!flag) + av_log(avf, AV_LOG_INFO, "unknown disposition type '%s'\n", value); + for (i = 0; i < avf->nb_streams; ++i) + if (stream_id == i || stream_id == -1) + avf->streams[i]->disposition |= flag; +} + +static int decode_info_header(NUTContext *nut) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + uint64_t tmp, chapter_start, chapter_len; + unsigned int stream_id_plus1, count; + int chapter_id, i; + int64_t value, end; + char name[256], str_value[1024], type_str[256]; + const char *type; + AVChapter *chapter = NULL; + AVStream *st = NULL; + AVDictionary **metadata = NULL; + + end = get_packetheader(nut, bc, 1, INFO_STARTCODE); + end += avio_tell(bc); + + GET_V(stream_id_plus1, tmp <= s->nb_streams); + chapter_id = get_s(bc); + chapter_start = ffio_read_varlen(bc); + chapter_len = ffio_read_varlen(bc); + count = ffio_read_varlen(bc); + + if (chapter_id && !stream_id_plus1) { + int64_t start = chapter_start / nut->time_base_count; + chapter = avpriv_new_chapter(s, chapter_id, + nut->time_base[chapter_start % + nut->time_base_count], + start, start + chapter_len, NULL); + metadata = &chapter->metadata; + } else if (stream_id_plus1) { + st = s->streams[stream_id_plus1 - 1]; + metadata = &st->metadata; + } else + metadata = &s->metadata; + + for (i = 0; i < count; i++) { + get_str(bc, name, sizeof(name)); + value = get_s(bc); + if (value == -1) { + type = "UTF-8"; + get_str(bc, str_value, sizeof(str_value)); + } else if (value == -2) { + get_str(bc, type_str, sizeof(type_str)); + type = type_str; + get_str(bc, str_value, sizeof(str_value)); + } else if (value == -3) { + type = "s"; + value = get_s(bc); + } else if (value == -4) { + type = "t"; + value = ffio_read_varlen(bc); + } else if (value < -4) { + type = "r"; + get_s(bc); + } else { + type = "v"; + } + + if (stream_id_plus1 > s->nb_streams) { + av_log(s, AV_LOG_ERROR, "invalid stream id for info packet\n"); + continue; + } + + if (!strcmp(type, "UTF-8")) { + if (chapter_id == 0 && !strcmp(name, "Disposition")) { + set_disposition_bits(s, str_value, stream_id_plus1 - 1); + continue; + } + + if (stream_id_plus1 && !strcmp(name, "r_frame_rate")) { + sscanf(str_value, "%d/%d", &st->r_frame_rate.num, &st->r_frame_rate.den); + if (st->r_frame_rate.num >= 1000LL*st->r_frame_rate.den) + st->r_frame_rate.num = st->r_frame_rate.den = 0; + continue; + } + + if (metadata && av_strcasecmp(name, "Uses") && + av_strcasecmp(name, "Depends") && av_strcasecmp(name, "Replaces")) + av_dict_set(metadata, name, str_value, 0); + } + } + + if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, "info header checksum mismatch\n"); + return -1; + } + return 0; +} + +static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + int64_t end; + uint64_t tmp; + + nut->last_syncpoint_pos = avio_tell(bc) - 8; + + end = get_packetheader(nut, bc, 1, SYNCPOINT_STARTCODE); + end += avio_tell(bc); + + tmp = ffio_read_varlen(bc); + *back_ptr = nut->last_syncpoint_pos - 16 * ffio_read_varlen(bc); + if (*back_ptr < 0) + return AVERROR_INVALIDDATA; + + ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count], + tmp / nut->time_base_count); + + if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n"); + return AVERROR_INVALIDDATA; + } + + *ts = tmp / nut->time_base_count * + av_q2d(nut->time_base[tmp % nut->time_base_count]) * AV_TIME_BASE; + ff_nut_add_sp(nut, nut->last_syncpoint_pos, *back_ptr, *ts); + + return 0; +} + +//FIXME calculate exactly, this is just a good approximation. +static int64_t find_duration(NUTContext *nut, int64_t filesize) +{ + AVFormatContext *s = nut->avf; + int64_t duration = 0; + + int64_t pos = FFMAX(0, filesize - 2*nut->max_distance); + for(;;){ + int64_t ts = nut_read_timestamp(s, -1, &pos, INT64_MAX); + if(ts < 0) + break; + duration = FFMAX(duration, ts); + pos++; + } + if(duration > 0) + s->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + return duration; +} + +static int find_and_decode_index(NUTContext *nut) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + uint64_t tmp, end; + int i, j, syncpoint_count; + int64_t filesize = avio_size(bc); + int64_t *syncpoints; + int8_t *has_keyframe; + int ret = -1; + + if(filesize <= 0) + return -1; + + avio_seek(bc, filesize - 12, SEEK_SET); + avio_seek(bc, filesize - avio_rb64(bc), SEEK_SET); + if (avio_rb64(bc) != INDEX_STARTCODE) { + av_log(s, AV_LOG_ERROR, "no index at the end\n"); + + if(s->duration<=0) + s->duration = find_duration(nut, filesize); + return -1; + } + + end = get_packetheader(nut, bc, 1, INDEX_STARTCODE); + end += avio_tell(bc); + + ffio_read_varlen(bc); // max_pts + GET_V(syncpoint_count, tmp < INT_MAX / 8 && tmp > 0); + syncpoints = av_malloc(sizeof(int64_t) * syncpoint_count); + has_keyframe = av_malloc(sizeof(int8_t) * (syncpoint_count + 1)); + for (i = 0; i < syncpoint_count; i++) { + syncpoints[i] = ffio_read_varlen(bc); + if (syncpoints[i] <= 0) + goto fail; + if (i) + syncpoints[i] += syncpoints[i - 1]; + } + + for (i = 0; i < s->nb_streams; i++) { + int64_t last_pts = -1; + for (j = 0; j < syncpoint_count;) { + uint64_t x = ffio_read_varlen(bc); + int type = x & 1; + int n = j; + x >>= 1; + if (type) { + int flag = x & 1; + x >>= 1; + if (n + x >= syncpoint_count + 1) { + av_log(s, AV_LOG_ERROR, "index overflow A %d + %"PRIu64" >= %d\n", n, x, syncpoint_count + 1); + goto fail; + } + while (x--) + has_keyframe[n++] = flag; + has_keyframe[n++] = !flag; + } else { + while (x != 1) { + if (n >= syncpoint_count + 1) { + av_log(s, AV_LOG_ERROR, "index overflow B\n"); + goto fail; + } + has_keyframe[n++] = x & 1; + x >>= 1; + } + } + if (has_keyframe[0]) { + av_log(s, AV_LOG_ERROR, "keyframe before first syncpoint in index\n"); + goto fail; + } + av_assert0(n <= syncpoint_count + 1); + for (; j < n && j < syncpoint_count; j++) { + if (has_keyframe[j]) { + uint64_t B, A = ffio_read_varlen(bc); + if (!A) { + A = ffio_read_varlen(bc); + B = ffio_read_varlen(bc); + // eor_pts[j][i] = last_pts + A + B + } else + B = 0; + av_add_index_entry(s->streams[i], 16 * syncpoints[j - 1], + last_pts + A, 0, 0, AVINDEX_KEYFRAME); + last_pts += A + B; + } + } + } + } + + if (skip_reserved(bc, end) || ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, "index checksum mismatch\n"); + goto fail; + } + ret = 0; + +fail: + av_free(syncpoints); + av_free(has_keyframe); + return ret; +} + +static int nut_read_header(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + AVIOContext *bc = s->pb; + int64_t pos; + int initialized_stream_count; + + nut->avf = s; + + /* main header */ + pos = 0; + do { + pos = find_startcode(bc, MAIN_STARTCODE, pos) + 1; + if (pos < 0 + 1) { + av_log(s, AV_LOG_ERROR, "No main startcode found.\n"); + return AVERROR_INVALIDDATA; + } + } while (decode_main_header(nut) < 0); + + /* stream headers */ + pos = 0; + for (initialized_stream_count = 0; initialized_stream_count < s->nb_streams;) { + pos = find_startcode(bc, STREAM_STARTCODE, pos) + 1; + if (pos < 0 + 1) { + av_log(s, AV_LOG_ERROR, "Not all stream headers found.\n"); + return AVERROR_INVALIDDATA; + } + if (decode_stream_header(nut) >= 0) + initialized_stream_count++; + } + + /* info headers */ + pos = 0; + for (;;) { + uint64_t startcode = find_any_startcode(bc, pos); + pos = avio_tell(bc); + + if (startcode == 0) { + av_log(s, AV_LOG_ERROR, "EOF before video frames\n"); + return AVERROR_INVALIDDATA; + } else if (startcode == SYNCPOINT_STARTCODE) { + nut->next_startcode = startcode; + break; + } else if (startcode != INFO_STARTCODE) { + continue; + } + + decode_info_header(nut); + } + + s->data_offset = pos - 8; + + if (bc->seekable) { + int64_t orig_pos = avio_tell(bc); + find_and_decode_index(nut); + avio_seek(bc, orig_pos, SEEK_SET); + } + av_assert0(nut->next_startcode == SYNCPOINT_STARTCODE); + + ff_metadata_conv_ctx(s, NULL, ff_nut_metadata_conv); + + return 0; +} + +static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id, + uint8_t *header_idx, int frame_code) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + StreamContext *stc; + int size, flags, size_mul, pts_delta, i, reserved_count; + uint64_t tmp; + + if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) { + av_log(s, AV_LOG_ERROR, + "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n", + avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance); + return AVERROR_INVALIDDATA; + } + + flags = nut->frame_code[frame_code].flags; + size_mul = nut->frame_code[frame_code].size_mul; + size = nut->frame_code[frame_code].size_lsb; + *stream_id = nut->frame_code[frame_code].stream_id; + pts_delta = nut->frame_code[frame_code].pts_delta; + reserved_count = nut->frame_code[frame_code].reserved_count; + *header_idx = nut->frame_code[frame_code].header_idx; + + if (flags & FLAG_INVALID) + return AVERROR_INVALIDDATA; + if (flags & FLAG_CODED) + flags ^= ffio_read_varlen(bc); + if (flags & FLAG_STREAM_ID) { + GET_V(*stream_id, tmp < s->nb_streams); + } + stc = &nut->stream[*stream_id]; + if (flags & FLAG_CODED_PTS) { + int coded_pts = ffio_read_varlen(bc); + // FIXME check last_pts validity? + if (coded_pts < (1 << stc->msb_pts_shift)) { + *pts = ff_lsb2full(stc, coded_pts); + } else + *pts = coded_pts - (1LL << stc->msb_pts_shift); + } else + *pts = stc->last_pts + pts_delta; + if (flags & FLAG_SIZE_MSB) + size += size_mul * ffio_read_varlen(bc); + if (flags & FLAG_MATCH_TIME) + get_s(bc); + if (flags & FLAG_HEADER_IDX) + *header_idx = ffio_read_varlen(bc); + if (flags & FLAG_RESERVED) + reserved_count = ffio_read_varlen(bc); + for (i = 0; i < reserved_count; i++) + ffio_read_varlen(bc); + + if (*header_idx >= (unsigned)nut->header_count) { + av_log(s, AV_LOG_ERROR, "header_idx invalid\n"); + return AVERROR_INVALIDDATA; + } + if (size > 4096) + *header_idx = 0; + size -= nut->header_len[*header_idx]; + + if (flags & FLAG_CHECKSUM) { + avio_rb32(bc); // FIXME check this + } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) > + stc->max_pts_distance) { + av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n"); + return AVERROR_INVALIDDATA; + } + + stc->last_pts = *pts; + stc->last_flags = flags; + + return size; +} + +static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code) +{ + AVFormatContext *s = nut->avf; + AVIOContext *bc = s->pb; + int size, stream_id, discard; + int64_t pts, last_IP_pts; + StreamContext *stc; + uint8_t header_idx; + + size = decode_frame_header(nut, &pts, &stream_id, &header_idx, frame_code); + if (size < 0) + return size; + + stc = &nut->stream[stream_id]; + + if (stc->last_flags & FLAG_KEY) + stc->skip_until_key_frame = 0; + + discard = s->streams[stream_id]->discard; + last_IP_pts = s->streams[stream_id]->last_IP_pts; + if ((discard >= AVDISCARD_NONKEY && !(stc->last_flags & FLAG_KEY)) || + (discard >= AVDISCARD_BIDIR && last_IP_pts != AV_NOPTS_VALUE && + last_IP_pts > pts) || + discard >= AVDISCARD_ALL || + stc->skip_until_key_frame) { + avio_skip(bc, size); + return 1; + } + + if (av_new_packet(pkt, size + nut->header_len[header_idx]) < 0) + return AVERROR(ENOMEM); + memcpy(pkt->data, nut->header[header_idx], nut->header_len[header_idx]); + pkt->pos = avio_tell(bc); // FIXME + avio_read(bc, pkt->data + nut->header_len[header_idx], size); + + pkt->stream_index = stream_id; + if (stc->last_flags & FLAG_KEY) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pts = pts; + + return 0; +} + +static int nut_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + NUTContext *nut = s->priv_data; + AVIOContext *bc = s->pb; + int i, frame_code = 0, ret, skip; + int64_t ts, back_ptr; + + for (;;) { + int64_t pos = avio_tell(bc); + uint64_t tmp = nut->next_startcode; + nut->next_startcode = 0; + + if (tmp) { + pos -= 8; + } else { + frame_code = avio_r8(bc); + if (url_feof(bc)) + return -1; + if (frame_code == 'N') { + tmp = frame_code; + for (i = 1; i < 8; i++) + tmp = (tmp << 8) + avio_r8(bc); + } + } + switch (tmp) { + case MAIN_STARTCODE: + case STREAM_STARTCODE: + case INDEX_STARTCODE: + skip = get_packetheader(nut, bc, 0, tmp); + avio_skip(bc, skip); + break; + case INFO_STARTCODE: + if (decode_info_header(nut) < 0) + goto resync; + break; + case SYNCPOINT_STARTCODE: + if (decode_syncpoint(nut, &ts, &back_ptr) < 0) + goto resync; + frame_code = avio_r8(bc); + case 0: + ret = decode_frame(nut, pkt, frame_code); + if (ret == 0) + return 0; + else if (ret == 1) // OK but discard packet + break; + default: +resync: + av_log(s, AV_LOG_DEBUG, "syncing from %"PRId64"\n", pos); + tmp = find_any_startcode(bc, nut->last_syncpoint_pos + 1); + if (tmp == 0) + return AVERROR_INVALIDDATA; + av_log(s, AV_LOG_DEBUG, "sync\n"); + nut->next_startcode = tmp; + } + } +} + +static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *pos_arg, int64_t pos_limit) +{ + NUTContext *nut = s->priv_data; + AVIOContext *bc = s->pb; + int64_t pos, pts, back_ptr; + av_log(s, AV_LOG_DEBUG, "read_timestamp(X,%d,%"PRId64",%"PRId64")\n", + stream_index, *pos_arg, pos_limit); + + pos = *pos_arg; + do { + pos = find_startcode(bc, SYNCPOINT_STARTCODE, pos) + 1; + if (pos < 1) { + av_log(s, AV_LOG_ERROR, "read_timestamp failed.\n"); + return AV_NOPTS_VALUE; + } + } while (decode_syncpoint(nut, &pts, &back_ptr) < 0); + *pos_arg = pos - 1; + av_assert0(nut->last_syncpoint_pos == *pos_arg); + + av_log(s, AV_LOG_DEBUG, "return %"PRId64" %"PRId64"\n", pts, back_ptr); + if (stream_index == -2) + return back_ptr; + av_assert0(stream_index == -1); + return pts; +} + +static int read_seek(AVFormatContext *s, int stream_index, + int64_t pts, int flags) +{ + NUTContext *nut = s->priv_data; + AVStream *st = s->streams[stream_index]; + Syncpoint dummy = { .ts = pts * av_q2d(st->time_base) * AV_TIME_BASE }; + Syncpoint nopts_sp = { .ts = AV_NOPTS_VALUE, .back_ptr = AV_NOPTS_VALUE }; + Syncpoint *sp, *next_node[2] = { &nopts_sp, &nopts_sp }; + int64_t pos, pos2, ts; + int i; + + if (st->index_entries) { + int index = av_index_search_timestamp(st, pts, flags); + if (index < 0) + index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD); + if (index < 0) + return -1; + + pos2 = st->index_entries[index].pos; + ts = st->index_entries[index].timestamp; + } else { + av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pts_cmp, + (void **) next_node); + av_log(s, AV_LOG_DEBUG, "%"PRIu64"-%"PRIu64" %"PRId64"-%"PRId64"\n", + next_node[0]->pos, next_node[1]->pos, next_node[0]->ts, + next_node[1]->ts); + pos = ff_gen_search(s, -1, dummy.ts, next_node[0]->pos, + next_node[1]->pos, next_node[1]->pos, + next_node[0]->ts, next_node[1]->ts, + AVSEEK_FLAG_BACKWARD, &ts, nut_read_timestamp); + + if (!(flags & AVSEEK_FLAG_BACKWARD)) { + dummy.pos = pos + 16; + next_node[1] = &nopts_sp; + av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, + (void **) next_node); + pos2 = ff_gen_search(s, -2, dummy.pos, next_node[0]->pos, + next_node[1]->pos, next_node[1]->pos, + next_node[0]->back_ptr, next_node[1]->back_ptr, + flags, &ts, nut_read_timestamp); + if (pos2 >= 0) + pos = pos2; + // FIXME dir but I think it does not matter + } + dummy.pos = pos; + sp = av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, + NULL); + + av_assert0(sp); + pos2 = sp->back_ptr - 15; + } + av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); + pos = find_startcode(s->pb, SYNCPOINT_STARTCODE, pos2); + avio_seek(s->pb, pos, SEEK_SET); + av_log(NULL, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos); + if (pos2 > pos || pos2 + 15 < pos) + av_log(NULL, AV_LOG_ERROR, "no syncpoint at backptr pos\n"); + for (i = 0; i < s->nb_streams; i++) + nut->stream[i].skip_until_key_frame = 1; + + return 0; +} + +static int nut_read_close(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + int i; + + av_freep(&nut->time_base); + av_freep(&nut->stream); + ff_nut_free_sp(nut); + for (i = 1; i < nut->header_count; i++) + av_freep(&nut->header[i]); + + return 0; +} + +AVInputFormat ff_nut_demuxer = { + .name = "nut", + .long_name = NULL_IF_CONFIG_SMALL("NUT"), + .flags = AVFMT_SEEK_TO_PTS, + .priv_data_size = sizeof(NUTContext), + .read_probe = nut_probe, + .read_header = nut_read_header, + .read_packet = nut_read_packet, + .read_close = nut_read_close, + .read_seek = read_seek, + .extensions = "nut", + .codec_tag = ff_nut_codec_tags, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nutdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nutdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,22 @@ +libavformat/nutdec.o libavformat/nutdec.o: libavformat/nutdec.c \ + libavutil/avstring.h libavutil/attributes.h libavutil/avassert.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/dict.h \ + libavutil/mathematics.h libavutil/tree.h libavformat/avio_internal.h \ + libavformat/avio.h libavutil/common.h libavutil/log.h \ + libavformat/version.h libavutil/avutil.h libavformat/url.h \ + libavformat/nut.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavformat/internal.h libavformat/metadata.h \ + libavformat/riff.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nutdec.o Binary file ffmpeg/libavformat/nutdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nutenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nutenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,1023 @@ +/* + * nut muxer + * Copyright (c) 2004-2007 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/tree.h" +#include "libavutil/dict.h" +#include "libavutil/avassert.h" +#include "libavcodec/mpegaudiodata.h" +#include "nut.h" +#include "internal.h" +#include "avio_internal.h" +#include "riff.h" + +static int find_expected_header(AVCodecContext *c, int size, int key_frame, + uint8_t out[64]) +{ + int sample_rate = c->sample_rate; + + if (size > 4096) + return 0; + + AV_WB24(out, 1); + + if (c->codec_id == AV_CODEC_ID_MPEG4) { + if (key_frame) { + return 3; + } else { + out[3] = 0xB6; + return 4; + } + } else if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO || + c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + return 3; + } else if (c->codec_id == AV_CODEC_ID_H264) { + return 3; + } else if (c->codec_id == AV_CODEC_ID_MP3 || + c->codec_id == AV_CODEC_ID_MP2) { + int lsf, mpeg25, sample_rate_index, bitrate_index, frame_size; + int layer = c->codec_id == AV_CODEC_ID_MP3 ? 3 : 2; + unsigned int header = 0xFFF00000; + + lsf = sample_rate < (24000 + 32000) / 2; + mpeg25 = sample_rate < (12000 + 16000) / 2; + sample_rate <<= lsf + mpeg25; + if (sample_rate < (32000 + 44100) / 2) sample_rate_index = 2; + else if (sample_rate < (44100 + 48000) / 2) sample_rate_index = 0; + else sample_rate_index = 1; + + sample_rate = avpriv_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25); + + for (bitrate_index = 2; bitrate_index < 30; bitrate_index++) { + frame_size = + avpriv_mpa_bitrate_tab[lsf][layer - 1][bitrate_index >> 1]; + frame_size = (frame_size * 144000) / (sample_rate << lsf) + + (bitrate_index & 1); + + if (frame_size == size) + break; + } + + header |= (!lsf) << 19; + header |= (4 - layer) << 17; + header |= 1 << 16; //no crc + AV_WB32(out, header); + if (size <= 0) + return 2; //we guess there is no crc, if there is one the user clearly does not care about overhead + if (bitrate_index == 30) + return -1; //something is wrong ... + + header |= (bitrate_index >> 1) << 12; + header |= sample_rate_index << 10; + header |= (bitrate_index & 1) << 9; + + return 2; //FIXME actually put the needed ones in build_elision_headers() + return 3; //we guess that the private bit is not set +//FIXME the above assumptions should be checked, if these turn out false too often something should be done + } + return 0; +} + +static int find_header_idx(AVFormatContext *s, AVCodecContext *c, int size, int frame_type) +{ + NUTContext *nut = s->priv_data; + uint8_t out[64]; + int i; + int len = find_expected_header(c, size, frame_type, out); + + for (i = 1; i < nut->header_count; i++) { + if (len == nut->header_len[i] && !memcmp(out, nut->header[i], len)) { + return i; + } + } + + return 0; +} + +static void build_elision_headers(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + int i; + //FIXME this is lame + //FIXME write a 2pass mode to find the maximal headers + static const uint8_t headers[][5] = { + { 3, 0x00, 0x00, 0x01 }, + { 4, 0x00, 0x00, 0x01, 0xB6}, + { 2, 0xFF, 0xFA }, //mp3+crc + { 2, 0xFF, 0xFB }, //mp3 + { 2, 0xFF, 0xFC }, //mp2+crc + { 2, 0xFF, 0xFD }, //mp2 + }; + + nut->header_count = 7; + for (i = 1; i < nut->header_count; i++) { + nut->header_len[i] = headers[i - 1][0]; + nut->header[i] = &headers[i - 1][1]; + } +} + +static void build_frame_code(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + int key_frame, index, pred, stream_id; + int start = 1; + int end = 254; + int keyframe_0_esc = s->nb_streams > 2; + int pred_table[10]; + FrameCode *ft; + + ft = &nut->frame_code[start]; + ft->flags = FLAG_CODED; + ft->size_mul = 1; + ft->pts_delta = 1; + start++; + + if (keyframe_0_esc) { + /* keyframe = 0 escape */ + FrameCode *ft = &nut->frame_code[start]; + ft->flags = FLAG_STREAM_ID | FLAG_SIZE_MSB | FLAG_CODED_PTS; + ft->size_mul = 1; + start++; + } + + for (stream_id = 0; stream_id < s->nb_streams; stream_id++) { + int start2 = start + (end - start) * stream_id / s->nb_streams; + int end2 = start + (end - start) * (stream_id + 1) / s->nb_streams; + AVCodecContext *codec = s->streams[stream_id]->codec; + int is_audio = codec->codec_type == AVMEDIA_TYPE_AUDIO; + int intra_only = /*codec->intra_only || */ is_audio; + int pred_count; + int frame_size = 0; + + if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { + frame_size = av_get_audio_frame_duration(codec, 0); + if (codec->codec_id == AV_CODEC_ID_VORBIS && !frame_size) + frame_size = 64; + } else { + AVRational f = av_div_q(codec->time_base, *nut->stream[stream_id].time_base); + if (f.den == 1 && f.num>0) + frame_size = f.num; + } + if (!frame_size) + frame_size = 1; + + for (key_frame = 0; key_frame < 2; key_frame++) { + if (!intra_only || !keyframe_0_esc || key_frame != 0) { + FrameCode *ft = &nut->frame_code[start2]; + ft->flags = FLAG_KEY * key_frame; + ft->flags |= FLAG_SIZE_MSB | FLAG_CODED_PTS; + ft->stream_id = stream_id; + ft->size_mul = 1; + if (is_audio) + ft->header_idx = find_header_idx(s, codec, -1, key_frame); + start2++; + } + } + + key_frame = intra_only; +#if 1 + if (is_audio) { + int frame_bytes = codec->frame_size * (int64_t)codec->bit_rate / + (8 * codec->sample_rate); + int pts; + for (pts = 0; pts < 2; pts++) { + for (pred = 0; pred < 2; pred++) { + FrameCode *ft = &nut->frame_code[start2]; + ft->flags = FLAG_KEY * key_frame; + ft->stream_id = stream_id; + ft->size_mul = frame_bytes + 2; + ft->size_lsb = frame_bytes + pred; + ft->pts_delta = pts * frame_size; + ft->header_idx = find_header_idx(s, codec, frame_bytes + pred, key_frame); + start2++; + } + } + } else { + FrameCode *ft = &nut->frame_code[start2]; + ft->flags = FLAG_KEY | FLAG_SIZE_MSB; + ft->stream_id = stream_id; + ft->size_mul = 1; + ft->pts_delta = frame_size; + start2++; + } +#endif + + if (codec->has_b_frames) { + pred_count = 5; + pred_table[0] = -2; + pred_table[1] = -1; + pred_table[2] = 1; + pred_table[3] = 3; + pred_table[4] = 4; + } else if (codec->codec_id == AV_CODEC_ID_VORBIS) { + pred_count = 3; + pred_table[0] = 2; + pred_table[1] = 9; + pred_table[2] = 16; + } else { + pred_count = 1; + pred_table[0] = 1; + } + + for (pred = 0; pred < pred_count; pred++) { + int start3 = start2 + (end2 - start2) * pred / pred_count; + int end3 = start2 + (end2 - start2) * (pred + 1) / pred_count; + + pred_table[pred] *= frame_size; + + for (index = start3; index < end3; index++) { + FrameCode *ft = &nut->frame_code[index]; + ft->flags = FLAG_KEY * key_frame; + ft->flags |= FLAG_SIZE_MSB; + ft->stream_id = stream_id; +//FIXME use single byte size and pred from last + ft->size_mul = end3 - start3; + ft->size_lsb = index - start3; + ft->pts_delta = pred_table[pred]; + if (is_audio) + ft->header_idx = find_header_idx(s, codec, -1, key_frame); + } + } + } + memmove(&nut->frame_code['N' + 1], &nut->frame_code['N'], sizeof(FrameCode) * (255 - 'N')); + nut->frame_code[0].flags = + nut->frame_code[255].flags = + nut->frame_code['N'].flags = FLAG_INVALID; +} + +static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, uint64_t val) +{ + val *= nut->time_base_count; + val += time_base - nut->time_base; + ff_put_v(bc, val); +} +/** + * Store a string as vb. + */ +static void put_str(AVIOContext *bc, const char *string) +{ + int len = strlen(string); + + ff_put_v(bc, len); + avio_write(bc, string, len); +} + +static void put_s(AVIOContext *bc, int64_t val) +{ + ff_put_v(bc, 2 * FFABS(val) - (val > 0)); +} + +#ifdef TRACE +static inline void ff_put_v_trace(AVIOContext *bc, uint64_t v, const char *file, + const char *func, int line) +{ + av_log(NULL, AV_LOG_DEBUG, "ff_put_v %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); + + ff_put_v(bc, v); +} + +static inline void put_s_trace(AVIOContext *bc, int64_t v, const char *file, const char *func, int line) +{ + av_log(NULL, AV_LOG_DEBUG, "put_s %5"PRId64" / %"PRIX64" in %s %s:%d\n", v, v, file, func, line); + + put_s(bc, v); +} +#define ff_put_v(bc, v) ff_put_v_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#define put_s(bc, v) put_s_trace(bc, v, __FILE__, __PRETTY_FUNCTION__, __LINE__) +#endif + +//FIXME remove calculate_checksum +static void put_packet(NUTContext *nut, AVIOContext *bc, AVIOContext *dyn_bc, + int calculate_checksum, uint64_t startcode) +{ + uint8_t *dyn_buf = NULL; + int dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + int forw_ptr = dyn_size + 4 * calculate_checksum; + + if (forw_ptr > 4096) + ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); + avio_wb64(bc, startcode); + ff_put_v(bc, forw_ptr); + if (forw_ptr > 4096) + avio_wl32(bc, ffio_get_checksum(bc)); + + if (calculate_checksum) + ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); + avio_write(bc, dyn_buf, dyn_size); + if (calculate_checksum) + avio_wl32(bc, ffio_get_checksum(bc)); + + av_free(dyn_buf); +} + +static void write_mainheader(NUTContext *nut, AVIOContext *bc) +{ + int i, j, tmp_pts, tmp_flags, tmp_stream, tmp_mul, tmp_size, tmp_fields, + tmp_head_idx; + int64_t tmp_match; + + ff_put_v(bc, 3); /* version */ + ff_put_v(bc, nut->avf->nb_streams); + ff_put_v(bc, nut->max_distance); + ff_put_v(bc, nut->time_base_count); + + for (i = 0; i < nut->time_base_count; i++) { + ff_put_v(bc, nut->time_base[i].num); + ff_put_v(bc, nut->time_base[i].den); + } + + tmp_pts = 0; + tmp_mul = 1; + tmp_stream = 0; + tmp_match = 1 - (1LL << 62); + tmp_head_idx = 0; + for (i = 0; i < 256; ) { + tmp_fields = 0; + tmp_size = 0; +// tmp_res=0; + if (tmp_pts != nut->frame_code[i].pts_delta ) tmp_fields = 1; + if (tmp_mul != nut->frame_code[i].size_mul ) tmp_fields = 2; + if (tmp_stream != nut->frame_code[i].stream_id ) tmp_fields = 3; + if (tmp_size != nut->frame_code[i].size_lsb ) tmp_fields = 4; +// if (tmp_res != nut->frame_code[i].res ) tmp_fields=5; + if (tmp_head_idx != nut->frame_code[i].header_idx) tmp_fields = 8; + + tmp_pts = nut->frame_code[i].pts_delta; + tmp_flags = nut->frame_code[i].flags; + tmp_stream = nut->frame_code[i].stream_id; + tmp_mul = nut->frame_code[i].size_mul; + tmp_size = nut->frame_code[i].size_lsb; +// tmp_res = nut->frame_code[i].res; + tmp_head_idx = nut->frame_code[i].header_idx; + + for (j = 0; i < 256; j++, i++) { + if (i == 'N') { + j--; + continue; + } + if (nut->frame_code[i].pts_delta != tmp_pts || + nut->frame_code[i].flags != tmp_flags || + nut->frame_code[i].stream_id != tmp_stream || + nut->frame_code[i].size_mul != tmp_mul || + nut->frame_code[i].size_lsb != tmp_size + j || +// nut->frame_code[i].res != tmp_res || + nut->frame_code[i].header_idx != tmp_head_idx) + break; + } + if (j != tmp_mul - tmp_size) + tmp_fields = 6; + + ff_put_v(bc, tmp_flags); + ff_put_v(bc, tmp_fields); + if (tmp_fields > 0) put_s(bc, tmp_pts); + if (tmp_fields > 1) ff_put_v(bc, tmp_mul); + if (tmp_fields > 2) ff_put_v(bc, tmp_stream); + if (tmp_fields > 3) ff_put_v(bc, tmp_size); + if (tmp_fields > 4) ff_put_v(bc, 0 /*tmp_res*/); + if (tmp_fields > 5) ff_put_v(bc, j); + if (tmp_fields > 6) ff_put_v(bc, tmp_match); + if (tmp_fields > 7) ff_put_v(bc, tmp_head_idx); + } + ff_put_v(bc, nut->header_count - 1); + for (i = 1; i < nut->header_count; i++) { + ff_put_v(bc, nut->header_len[i]); + avio_write(bc, nut->header[i], nut->header_len[i]); + } +} + +static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, + AVStream *st, int i) +{ + NUTContext *nut = avctx->priv_data; + AVCodecContext *codec = st->codec; + + ff_put_v(bc, i); + switch (codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: ff_put_v(bc, 0); break; + case AVMEDIA_TYPE_AUDIO: ff_put_v(bc, 1); break; + case AVMEDIA_TYPE_SUBTITLE: ff_put_v(bc, 2); break; + default: ff_put_v(bc, 3); break; + } + ff_put_v(bc, 4); + if (codec->codec_tag) { + avio_wl32(bc, codec->codec_tag); + } else { + av_log(avctx, AV_LOG_ERROR, "No codec tag defined for stream %d\n", i); + return AVERROR(EINVAL); + } + + ff_put_v(bc, nut->stream[i].time_base - nut->time_base); + ff_put_v(bc, nut->stream[i].msb_pts_shift); + ff_put_v(bc, nut->stream[i].max_pts_distance); + ff_put_v(bc, codec->has_b_frames); + avio_w8(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */ + + ff_put_v(bc, codec->extradata_size); + avio_write(bc, codec->extradata, codec->extradata_size); + + switch (codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + ff_put_v(bc, codec->sample_rate); + ff_put_v(bc, 1); + ff_put_v(bc, codec->channels); + break; + case AVMEDIA_TYPE_VIDEO: + ff_put_v(bc, codec->width); + ff_put_v(bc, codec->height); + + if (st->sample_aspect_ratio.num <= 0 || + st->sample_aspect_ratio.den <= 0) { + ff_put_v(bc, 0); + ff_put_v(bc, 0); + } else { + ff_put_v(bc, st->sample_aspect_ratio.num); + ff_put_v(bc, st->sample_aspect_ratio.den); + } + ff_put_v(bc, 0); /* csp type -- unknown */ + break; + default: + break; + } + return 0; +} + +static int add_info(AVIOContext *bc, const char *type, const char *value) +{ + put_str(bc, type); + put_s(bc, -1); + put_str(bc, value); + return 1; +} + +static int write_globalinfo(NUTContext *nut, AVIOContext *bc) +{ + AVFormatContext *s = nut->avf; + AVDictionaryEntry *t = NULL; + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + int count = 0, dyn_size; + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) + count += add_info(dyn_bc, t->key, t->value); + + ff_put_v(bc, 0); //stream_if_plus1 + ff_put_v(bc, 0); //chapter_id + ff_put_v(bc, 0); //timestamp_start + ff_put_v(bc, 0); //length + + ff_put_v(bc, count); + + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + avio_write(bc, dyn_buf, dyn_size); + av_free(dyn_buf); + return 0; +} + +static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id) { + AVFormatContext *s= nut->avf; + AVStream* st = s->streams[stream_id]; + AVDictionaryEntry *t = NULL; + AVIOContext *dyn_bc; + uint8_t *dyn_buf=NULL; + int count=0, dyn_size, i; + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + while ((t = av_dict_get(st->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) + count += add_info(dyn_bc, t->key, t->value); + for (i=0; ff_nut_dispositions[i].flag; ++i) { + if (st->disposition & ff_nut_dispositions[i].flag) + count += add_info(dyn_bc, "Disposition", ff_nut_dispositions[i].str); + } + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + uint8_t buf[256]; + snprintf(buf, sizeof(buf), "%d/%d", st->codec->time_base.den, st->codec->time_base.num); + count += add_info(dyn_bc, "r_frame_rate", buf); + } + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + + if (count) { + ff_put_v(bc, stream_id + 1); //stream_id_plus1 + ff_put_v(bc, 0); //chapter_id + ff_put_v(bc, 0); //timestamp_start + ff_put_v(bc, 0); //length + + ff_put_v(bc, count); + + avio_write(bc, dyn_buf, dyn_size); + } + + av_free(dyn_buf); + return count; +} + +static int write_chapter(NUTContext *nut, AVIOContext *bc, int id) +{ + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + AVDictionaryEntry *t = NULL; + AVChapter *ch = nut->avf->chapters[id]; + int ret, dyn_size, count = 0; + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + ff_put_v(bc, 0); // stream_id_plus1 + put_s(bc, id + 1); // chapter_id + put_tt(nut, nut->chapter[id].time_base, bc, ch->start); // chapter_start + ff_put_v(bc, ch->end - ch->start); // chapter_len + + while ((t = av_dict_get(ch->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) + count += add_info(dyn_bc, t->key, t->value); + + ff_put_v(bc, count); + + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + avio_write(bc, dyn_buf, dyn_size); + av_freep(&dyn_buf); + return 0; +} + +static int write_index(NUTContext *nut, AVIOContext *bc) { + int i; + Syncpoint dummy= { .pos= 0 }; + Syncpoint *next_node[2] = { NULL }; + int64_t startpos = avio_tell(bc); + int64_t payload_size; + + put_tt(nut, nut->max_pts_tb, bc, nut->max_pts); + + ff_put_v(bc, nut->sp_count); + + for (i=0; isp_count; i++) { + av_tree_find(nut->syncpoints, &dummy, (void *) ff_nut_sp_pos_cmp, (void**)next_node); + ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4)); + dummy.pos = next_node[1]->pos; + } + + for (i=0; iavf->nb_streams; i++) { + StreamContext *nus= &nut->stream[i]; + int64_t last_pts= -1; + int j, k; + for (j=0; jsp_count; j++) { + int flag = (nus->keyframe_pts[j] != AV_NOPTS_VALUE) ^ (j+1 == nut->sp_count); + int n = 0; + for (; jsp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++) + n++; + + ff_put_v(bc, 1 + 2*flag + 4*n); + for (k= j - n; k<=j && ksp_count; k++) { + if (nus->keyframe_pts[k] == AV_NOPTS_VALUE) + continue; + av_assert0(nus->keyframe_pts[k] > last_pts); + ff_put_v(bc, nus->keyframe_pts[k] - last_pts); + last_pts = nus->keyframe_pts[k]; + } + } + } + + payload_size = avio_tell(bc) - startpos + 8 + 4; + + avio_wb64(bc, 8 + payload_size + av_log2(payload_size) / 7 + 1 + 4*(payload_size > 4096)); + + return 0; +} + +static int write_headers(AVFormatContext *avctx, AVIOContext *bc) +{ + NUTContext *nut = avctx->priv_data; + AVIOContext *dyn_bc; + int i, ret; + + ff_metadata_conv_ctx(avctx, ff_nut_metadata_conv, NULL); + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + write_mainheader(nut, dyn_bc); + put_packet(nut, bc, dyn_bc, 1, MAIN_STARTCODE); + + for (i = 0; i < nut->avf->nb_streams; i++) { + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + ret = write_streamheader(avctx, dyn_bc, nut->avf->streams[i], i); + if (ret < 0) + return ret; + put_packet(nut, bc, dyn_bc, 1, STREAM_STARTCODE); + } + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + write_globalinfo(nut, dyn_bc); + put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); + + for (i = 0; i < nut->avf->nb_streams; i++) { + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + ret = write_streaminfo(nut, dyn_bc, i); + if (ret < 0) + return ret; + if (ret > 0) + put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); + else { + uint8_t *buf; + avio_close_dyn_buf(dyn_bc, &buf); + av_free(buf); + } + } + + for (i = 0; i < nut->avf->nb_chapters; i++) { + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + ret = write_chapter(nut, dyn_bc, i); + if (ret < 0) { + uint8_t *buf; + avio_close_dyn_buf(dyn_bc, &buf); + av_freep(&buf); + return ret; + } + put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); + } + + nut->last_syncpoint_pos = INT_MIN; + nut->header_count++; + return 0; +} + +static int nut_write_header(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + AVIOContext *bc = s->pb; + int i, j, ret; + + nut->avf = s; + + nut->stream = av_mallocz(sizeof(StreamContext ) * s->nb_streams); + nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters); + nut->time_base= av_mallocz(sizeof(AVRational ) *(s->nb_streams + + s->nb_chapters)); + if (!nut->stream || !nut->chapter || !nut->time_base) { + av_freep(&nut->stream); + av_freep(&nut->chapter); + av_freep(&nut->time_base); + return AVERROR(ENOMEM); + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + int ssize; + AVRational time_base; + ff_parse_specific_params(st->codec, &time_base.den, &ssize, &time_base.num); + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate) { + time_base = (AVRational) {1, st->codec->sample_rate}; + } else { + time_base = ff_choose_timebase(s, st, 48000); + } + + avpriv_set_pts_info(st, 64, time_base.num, time_base.den); + + for (j = 0; j < nut->time_base_count; j++) + if (!memcmp(&time_base, &nut->time_base[j], sizeof(AVRational))) { + break; + } + nut->time_base[j] = time_base; + nut->stream[i].time_base = &nut->time_base[j]; + if (j == nut->time_base_count) + nut->time_base_count++; + + if (INT64_C(1000) * time_base.num >= time_base.den) + nut->stream[i].msb_pts_shift = 7; + else + nut->stream[i].msb_pts_shift = 14; + nut->stream[i].max_pts_distance = + FFMAX(time_base.den, time_base.num) / time_base.num; + } + + for (i = 0; i < s->nb_chapters; i++) { + AVChapter *ch = s->chapters[i]; + + for (j = 0; j < nut->time_base_count; j++) + if (!memcmp(&ch->time_base, &nut->time_base[j], sizeof(AVRational))) + break; + + nut->time_base[j] = ch->time_base; + nut->chapter[i].time_base = &nut->time_base[j]; + if (j == nut->time_base_count) + nut->time_base_count++; + } + + nut->max_distance = MAX_DISTANCE; + build_elision_headers(s); + build_frame_code(s); + av_assert0(nut->frame_code['N'].flags == FLAG_INVALID); + + avio_write(bc, ID_STRING, strlen(ID_STRING)); + avio_w8(bc, 0); + + if ((ret = write_headers(s, bc)) < 0) + return ret; + + if (s->avoid_negative_ts < 0) + s->avoid_negative_ts = 1; + + avio_flush(bc); + + return 0; +} + +static int get_needed_flags(NUTContext *nut, StreamContext *nus, FrameCode *fc, + AVPacket *pkt) +{ + int flags = 0; + + if (pkt->flags & AV_PKT_FLAG_KEY) + flags |= FLAG_KEY; + if (pkt->stream_index != fc->stream_id) + flags |= FLAG_STREAM_ID; + if (pkt->size / fc->size_mul) + flags |= FLAG_SIZE_MSB; + if (pkt->pts - nus->last_pts != fc->pts_delta) + flags |= FLAG_CODED_PTS; + if (pkt->size > 2 * nut->max_distance) + flags |= FLAG_CHECKSUM; + if (FFABS(pkt->pts - nus->last_pts) > nus->max_pts_distance) + flags |= FLAG_CHECKSUM; + if (pkt->size < nut->header_len[fc->header_idx] || + (pkt->size > 4096 && fc->header_idx) || + memcmp(pkt->data, nut->header[fc->header_idx], + nut->header_len[fc->header_idx])) + flags |= FLAG_HEADER_IDX; + + return flags | (fc->flags & FLAG_CODED); +} + +static int find_best_header_idx(NUTContext *nut, AVPacket *pkt) +{ + int i; + int best_i = 0; + int best_len = 0; + + if (pkt->size > 4096) + return 0; + + for (i = 1; i < nut->header_count; i++) + if (pkt->size >= nut->header_len[i] + && nut->header_len[i] > best_len + && !memcmp(pkt->data, nut->header[i], nut->header_len[i])) { + best_i = i; + best_len = nut->header_len[i]; + } + return best_i; +} + +static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + NUTContext *nut = s->priv_data; + StreamContext *nus = &nut->stream[pkt->stream_index]; + AVIOContext *bc = s->pb, *dyn_bc; + FrameCode *fc; + int64_t coded_pts; + int best_length, frame_code, flags, needed_flags, i, header_idx; + int best_header_idx; + int key_frame = !!(pkt->flags & AV_PKT_FLAG_KEY); + int store_sp = 0; + int ret; + + if (pkt->pts < 0) { + av_log(s, AV_LOG_ERROR, + "Negative pts not supported stream %d, pts %"PRId64"\n", + pkt->stream_index, pkt->pts); + return AVERROR(EINVAL); + } + + if (1LL << (20 + 3 * nut->header_count) <= avio_tell(bc)) + write_headers(s, bc); + + if (key_frame && !(nus->last_flags & FLAG_KEY)) + store_sp = 1; + + if (pkt->size + 30 /*FIXME check*/ + avio_tell(bc) >= nut->last_syncpoint_pos + nut->max_distance) + store_sp = 1; + +//FIXME: Ensure store_sp is 1 in the first place. + + if (store_sp) { + Syncpoint *sp, dummy = { .pos = INT64_MAX }; + + ff_nut_reset_ts(nut, *nus->time_base, pkt->dts); + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + int64_t dts_tb = av_rescale_rnd(pkt->dts, + nus->time_base->num * (int64_t)nut->stream[i].time_base->den, + nus->time_base->den * (int64_t)nut->stream[i].time_base->num, + AV_ROUND_DOWN); + int index = av_index_search_timestamp(st, dts_tb, + AVSEEK_FLAG_BACKWARD); + if (index >= 0) + dummy.pos = FFMIN(dummy.pos, st->index_entries[index].pos); + } + if (dummy.pos == INT64_MAX) + dummy.pos = 0; + sp = av_tree_find(nut->syncpoints, &dummy, (void *)ff_nut_sp_pos_cmp, + NULL); + + nut->last_syncpoint_pos = avio_tell(bc); + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + put_tt(nut, nus->time_base, dyn_bc, pkt->dts); + ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0); + put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); + + ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts); + + if ((1ll<<60) % nut->sp_count == 0) + for (i=0; inb_streams; i++) { + int j; + StreamContext *nus = &nut->stream[i]; + nus->keyframe_pts = av_realloc(nus->keyframe_pts, 2*nut->sp_count*sizeof(*nus->keyframe_pts)); + if (!nus->keyframe_pts) + return AVERROR(ENOMEM); + for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++) + nus->keyframe_pts[j] = AV_NOPTS_VALUE; + } + } + av_assert0(nus->last_pts != AV_NOPTS_VALUE); + + coded_pts = pkt->pts & ((1 << nus->msb_pts_shift) - 1); + if (ff_lsb2full(nus, coded_pts) != pkt->pts) + coded_pts = pkt->pts + (1 << nus->msb_pts_shift); + + best_header_idx = find_best_header_idx(nut, pkt); + + best_length = INT_MAX; + frame_code = -1; + for (i = 0; i < 256; i++) { + int length = 0; + FrameCode *fc = &nut->frame_code[i]; + int flags = fc->flags; + + if (flags & FLAG_INVALID) + continue; + needed_flags = get_needed_flags(nut, nus, fc, pkt); + + if (flags & FLAG_CODED) { + length++; + flags = needed_flags; + } + + if ((flags & needed_flags) != needed_flags) + continue; + + if ((flags ^ needed_flags) & FLAG_KEY) + continue; + + if (flags & FLAG_STREAM_ID) + length += ff_get_v_length(pkt->stream_index); + + if (pkt->size % fc->size_mul != fc->size_lsb) + continue; + if (flags & FLAG_SIZE_MSB) + length += ff_get_v_length(pkt->size / fc->size_mul); + + if (flags & FLAG_CHECKSUM) + length += 4; + + if (flags & FLAG_CODED_PTS) + length += ff_get_v_length(coded_pts); + + if ( (flags & FLAG_CODED) + && nut->header_len[best_header_idx] > nut->header_len[fc->header_idx] + 1) { + flags |= FLAG_HEADER_IDX; + } + + if (flags & FLAG_HEADER_IDX) { + length += 1 - nut->header_len[best_header_idx]; + } else { + length -= nut->header_len[fc->header_idx]; + } + + length *= 4; + length += !(flags & FLAG_CODED_PTS); + length += !(flags & FLAG_CHECKSUM); + + if (length < best_length) { + best_length = length; + frame_code = i; + } + } + av_assert0(frame_code != -1); + fc = &nut->frame_code[frame_code]; + flags = fc->flags; + needed_flags = get_needed_flags(nut, nus, fc, pkt); + header_idx = fc->header_idx; + + ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); + avio_w8(bc, frame_code); + if (flags & FLAG_CODED) { + ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED)); + flags = needed_flags; + } + if (flags & FLAG_STREAM_ID) ff_put_v(bc, pkt->stream_index); + if (flags & FLAG_CODED_PTS) ff_put_v(bc, coded_pts); + if (flags & FLAG_SIZE_MSB ) ff_put_v(bc, pkt->size / fc->size_mul); + if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx); + + if (flags & FLAG_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc)); + else ffio_get_checksum(bc); + + avio_write(bc, pkt->data + nut->header_len[header_idx], pkt->size - nut->header_len[header_idx]); + nus->last_flags = flags; + nus->last_pts = pkt->pts; + + //FIXME just store one per syncpoint + if (flags & FLAG_KEY) { + av_add_index_entry( + s->streams[pkt->stream_index], + nut->last_syncpoint_pos, + pkt->pts, + 0, + 0, + AVINDEX_KEYFRAME); + if (nus->keyframe_pts && nus->keyframe_pts[nut->sp_count] == AV_NOPTS_VALUE) + nus->keyframe_pts[nut->sp_count] = pkt->pts; + } + + if (!nut->max_pts_tb || av_compare_ts(nut->max_pts, *nut->max_pts_tb, pkt->pts, *nus->time_base) < 0) { + nut->max_pts = pkt->pts; + nut->max_pts_tb = nus->time_base; + } + + return 0; +} + +static int nut_write_trailer(AVFormatContext *s) +{ + NUTContext *nut = s->priv_data; + AVIOContext *bc = s->pb, *dyn_bc; + int i, ret; + + while (nut->header_count < 3) + write_headers(s, bc); + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret >= 0 && nut->sp_count) { + write_index(nut, dyn_bc); + put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE); + } + + ff_nut_free_sp(nut); + for (i=0; inb_streams; i++) + av_freep(&nut->stream[i].keyframe_pts); + + av_freep(&nut->stream); + av_freep(&nut->chapter); + av_freep(&nut->time_base); + + return 0; +} + +AVOutputFormat ff_nut_muxer = { + .name = "nut", + .long_name = NULL_IF_CONFIG_SMALL("NUT"), + .mime_type = "video/x-nut", + .extensions = "nut", + .priv_data_size = sizeof(NUTContext), + .audio_codec = CONFIG_LIBVORBIS ? AV_CODEC_ID_VORBIS : + CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_MP2, + .video_codec = AV_CODEC_ID_MPEG4, + .write_header = nut_write_header, + .write_packet = nut_write_packet, + .write_trailer = nut_write_trailer, + .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS, + .codec_tag = ff_nut_codec_tags, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nutenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nutenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/nutenc.o libavformat/nutenc.o: libavformat/nutenc.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/tree.h libavutil/version.h libavutil/dict.h \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/mathematics.h libavutil/intfloat_readwrite.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/version.h \ + libavutil/old_pix_fmts.h libavcodec/mpegaudiodata.h \ + libavutil/internal.h libavformat/nut.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/metadata.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/riff.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nutenc.o Binary file ffmpeg/libavformat/nutenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nuv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nuv.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,392 @@ +/* + * NuppelVideo demuxer. + * Copyright (c) 2006 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" +#include "avformat.h" +#include "internal.h" +#include "riff.h" + +static const AVCodecTag nuv_audio_tags[] = { + { AV_CODEC_ID_PCM_S16LE, MKTAG('R', 'A', 'W', 'A') }, + { AV_CODEC_ID_MP3, MKTAG('L', 'A', 'M', 'E') }, + { AV_CODEC_ID_NONE, 0 }, +}; + +typedef struct { + int v_id; + int a_id; + int rtjpg_video; +} NUVContext; + +typedef enum { + NUV_VIDEO = 'V', + NUV_EXTRADATA = 'D', + NUV_AUDIO = 'A', + NUV_SEEKP = 'R', + NUV_MYTHEXT = 'X' +} nuv_frametype; + +static int nuv_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, "NuppelVideo", 12)) + return AVPROBE_SCORE_MAX; + if (!memcmp(p->buf, "MythTVVideo", 12)) + return AVPROBE_SCORE_MAX; + return 0; +} + +/// little macro to sanitize packet size +#define PKTSIZE(s) (s & 0xffffff) + +/** + * @brief read until we found all data needed for decoding + * @param vst video stream of which to change parameters + * @param ast video stream of which to change parameters + * @param myth set if this is a MythTVVideo format file + * @return 0 or AVERROR code + */ +static int get_codec_data(AVIOContext *pb, AVStream *vst, + AVStream *ast, int myth) +{ + nuv_frametype frametype; + + if (!vst && !myth) + return 1; // no codec data needed + while (!url_feof(pb)) { + int size, subtype; + + frametype = avio_r8(pb); + switch (frametype) { + case NUV_EXTRADATA: + subtype = avio_r8(pb); + avio_skip(pb, 6); + size = PKTSIZE(avio_rl32(pb)); + if (vst && subtype == 'R') { + if (vst->codec->extradata) { + av_freep(&vst->codec->extradata); + vst->codec->extradata_size = 0; + } + vst->codec->extradata = av_malloc(size); + if (!vst->codec->extradata) + return AVERROR(ENOMEM); + vst->codec->extradata_size = size; + avio_read(pb, vst->codec->extradata, size); + size = 0; + if (!myth) + return 0; + } + break; + case NUV_MYTHEXT: + avio_skip(pb, 7); + size = PKTSIZE(avio_rl32(pb)); + if (size != 128 * 4) + break; + avio_rl32(pb); // version + if (vst) { + vst->codec->codec_tag = avio_rl32(pb); + vst->codec->codec_id = + ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag); + if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G')) + vst->codec->codec_id = AV_CODEC_ID_NUV; + } else + avio_skip(pb, 4); + + if (ast) { + int id; + + ast->codec->codec_tag = avio_rl32(pb); + ast->codec->sample_rate = avio_rl32(pb); + ast->codec->bits_per_coded_sample = avio_rl32(pb); + ast->codec->channels = avio_rl32(pb); + ast->codec->channel_layout = 0; + + id = ff_wav_codec_get_id(ast->codec->codec_tag, + ast->codec->bits_per_coded_sample); + if (id == AV_CODEC_ID_NONE) { + id = ff_codec_get_id(nuv_audio_tags, ast->codec->codec_tag); + if (id == AV_CODEC_ID_PCM_S16LE) + id = ff_get_pcm_codec_id(ast->codec->bits_per_coded_sample, + 0, 0, ~1); + } + ast->codec->codec_id = id; + + ast->need_parsing = AVSTREAM_PARSE_FULL; + } else + avio_skip(pb, 4 * 4); + + size -= 6 * 4; + avio_skip(pb, size); + return 0; + case NUV_SEEKP: + size = 11; + break; + default: + avio_skip(pb, 7); + size = PKTSIZE(avio_rl32(pb)); + break; + } + avio_skip(pb, size); + } + + return 0; +} + +static int nuv_header(AVFormatContext *s) +{ + NUVContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + char id_string[12]; + double aspect, fps; + int is_mythtv, width, height, v_packs, a_packs, ret; + AVStream *vst = NULL, *ast = NULL; + + avio_read(pb, id_string, 12); + is_mythtv = !memcmp(id_string, "MythTVVideo", 12); + avio_skip(pb, 5); // version string + avio_skip(pb, 3); // padding + width = avio_rl32(pb); + height = avio_rl32(pb); + avio_rl32(pb); // unused, "desiredwidth" + avio_rl32(pb); // unused, "desiredheight" + avio_r8(pb); // 'P' == progressive, 'I' == interlaced + avio_skip(pb, 3); // padding + aspect = av_int2double(avio_rl64(pb)); + if (aspect > 0.9999 && aspect < 1.0001) + aspect = 4.0 / 3.0; + fps = av_int2double(avio_rl64(pb)); + + // number of packets per stream type, -1 means unknown, e.g. streaming + v_packs = avio_rl32(pb); + a_packs = avio_rl32(pb); + avio_rl32(pb); // text + + avio_rl32(pb); // keyframe distance (?) + + if (v_packs) { + vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + ctx->v_id = vst->index; + + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_id = AV_CODEC_ID_NUV; + vst->codec->width = width; + vst->codec->height = height; + vst->codec->bits_per_coded_sample = 10; + vst->sample_aspect_ratio = av_d2q(aspect * height / width, + 10000); +#if FF_API_R_FRAME_RATE + vst->r_frame_rate = +#endif + vst->avg_frame_rate = av_d2q(fps, 60000); + avpriv_set_pts_info(vst, 32, 1, 1000); + } else + ctx->v_id = -1; + + if (a_packs) { + ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ctx->a_id = ast->index; + + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + ast->codec->sample_rate = 44100; + ast->codec->bit_rate = 2 * 2 * 44100 * 8; + ast->codec->block_align = 2 * 2; + ast->codec->bits_per_coded_sample = 16; + avpriv_set_pts_info(ast, 32, 1, 1000); + } else + ctx->a_id = -1; + + if ((ret = get_codec_data(pb, vst, ast, is_mythtv)) < 0) + return ret; + + ctx->rtjpg_video = vst && vst->codec->codec_id == AV_CODEC_ID_NUV; + + return 0; +} + +#define HDRSIZE 12 + +static int nuv_packet(AVFormatContext *s, AVPacket *pkt) +{ + NUVContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t hdr[HDRSIZE]; + nuv_frametype frametype; + int ret, size; + + while (!url_feof(pb)) { + int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0; + uint64_t pos = avio_tell(pb); + + ret = avio_read(pb, hdr, HDRSIZE); + if (ret < HDRSIZE) + return ret < 0 ? ret : AVERROR(EIO); + + frametype = hdr[0]; + size = PKTSIZE(AV_RL32(&hdr[8])); + + switch (frametype) { + case NUV_EXTRADATA: + if (!ctx->rtjpg_video) { + avio_skip(pb, size); + break; + } + case NUV_VIDEO: + if (ctx->v_id < 0) { + av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n"); + avio_skip(pb, size); + break; + } + ret = av_new_packet(pkt, copyhdrsize + size); + if (ret < 0) + return ret; + + pkt->pos = pos; + pkt->flags |= hdr[2] == 0 ? AV_PKT_FLAG_KEY : 0; + pkt->pts = AV_RL32(&hdr[4]); + pkt->stream_index = ctx->v_id; + memcpy(pkt->data, hdr, copyhdrsize); + ret = avio_read(pb, pkt->data + copyhdrsize, size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + if (ret < size) + av_shrink_packet(pkt, copyhdrsize + ret); + return 0; + case NUV_AUDIO: + if (ctx->a_id < 0) { + av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n"); + avio_skip(pb, size); + break; + } + ret = av_get_packet(pb, pkt, size); + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->pos = pos; + pkt->pts = AV_RL32(&hdr[4]); + pkt->stream_index = ctx->a_id; + if (ret < 0) + return ret; + return 0; + case NUV_SEEKP: + // contains no data, size value is invalid + break; + default: + avio_skip(pb, size); + break; + } + } + + return AVERROR(EIO); +} + +/** + * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading + * \return 1 if the syncword is found 0 otherwise. + */ +static int nuv_resync(AVFormatContext *s, int64_t pos_limit) { + AVIOContext *pb = s->pb; + uint32_t tag = 0; + while(!url_feof(pb) && avio_tell(pb) < pos_limit) { + tag = (tag << 8) | avio_r8(pb); + if (tag == MKBETAG('R','T','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') && + (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j')) + return 1; + } + return 0; +} + +/** + * \brief attempts to read a timestamp from stream at the given stream position + * \return timestamp if successful and AV_NOPTS_VALUE if failure + */ +static int64_t nuv_read_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + NUVContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t hdr[HDRSIZE]; + nuv_frametype frametype; + int size, key, idx; + int64_t pos, dts; + + if (avio_seek(pb, *ppos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + + if (!nuv_resync(s, pos_limit)) + return AV_NOPTS_VALUE; + + while (!url_feof(pb) && avio_tell(pb) < pos_limit) { + if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE) + return AV_NOPTS_VALUE; + frametype = hdr[0]; + size = PKTSIZE(AV_RL32(&hdr[8])); + switch (frametype) { + case NUV_SEEKP: + break; + case NUV_AUDIO: + case NUV_VIDEO: + if (frametype == NUV_VIDEO) { + idx = ctx->v_id; + key = hdr[2] == 0; + } else { + idx = ctx->a_id; + key = 1; + } + if (stream_index == idx) { + + pos = avio_tell(s->pb) - HDRSIZE; + dts = AV_RL32(&hdr[4]); + + // TODO - add general support in av_gen_search, so it adds positions after reading timestamps + av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0, + key ? AVINDEX_KEYFRAME : 0); + + *ppos = pos; + return dts; + } + default: + avio_skip(pb, size); + break; + } + } + return AV_NOPTS_VALUE; +} + + +AVInputFormat ff_nuv_demuxer = { + .name = "nuv", + .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"), + .priv_data_size = sizeof(NUVContext), + .read_probe = nuv_probe, + .read_header = nuv_header, + .read_packet = nuv_packet, + .read_timestamp = nuv_read_dts, + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nuv.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/nuv.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/nuv.o libavformat/nuv.o: libavformat/nuv.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavutil/intfloat.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/riff.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/nuv.o Binary file ffmpeg/libavformat/nuv.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,858 @@ +/* + * Ogg bitstream support + * Luca Barbato + * Based on tcvp implementation + */ + +/* + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + */ + +#include +#include "libavutil/avassert.h" +#include "oggdec.h" +#include "avformat.h" +#include "internal.h" +#include "vorbiscomment.h" + +#define MAX_PAGE_SIZE 65307 +#define DECODER_BUFFER_SIZE MAX_PAGE_SIZE + +static const struct ogg_codec * const ogg_codecs[] = { + &ff_skeleton_codec, + &ff_dirac_codec, + &ff_speex_codec, + &ff_vorbis_codec, + &ff_theora_codec, + &ff_flac_codec, + &ff_celt_codec, + &ff_opus_codec, + &ff_old_dirac_codec, + &ff_old_flac_codec, + &ff_ogm_video_codec, + &ff_ogm_audio_codec, + &ff_ogm_text_codec, + &ff_ogm_old_codec, + NULL +}; + +static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts); +static int ogg_new_stream(AVFormatContext *s, uint32_t serial); + +//FIXME We could avoid some structure duplication +static int ogg_save(AVFormatContext *s) +{ + struct ogg *ogg = s->priv_data; + struct ogg_state *ost = + av_malloc(sizeof(*ost) + (ogg->nstreams - 1) * sizeof(*ogg->streams)); + int i; + ost->pos = avio_tell(s->pb); + ost->curidx = ogg->curidx; + ost->next = ogg->state; + ost->nstreams = ogg->nstreams; + memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); + + for (i = 0; i < ogg->nstreams; i++) { + struct ogg_stream *os = ogg->streams + i; + os->buf = av_mallocz(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); + memcpy(os->buf, ost->streams[i].buf, os->bufpos); + } + + ogg->state = ost; + + return 0; +} + +static int ogg_restore(AVFormatContext *s, int discard) +{ + struct ogg *ogg = s->priv_data; + AVIOContext *bc = s->pb; + struct ogg_state *ost = ogg->state; + int i; + + if (!ost) + return 0; + + ogg->state = ost->next; + + if (!discard) { + struct ogg_stream *old_streams = ogg->streams; + + for (i = 0; i < ogg->nstreams; i++) + av_free(ogg->streams[i].buf); + + avio_seek(bc, ost->pos, SEEK_SET); + ogg->page_pos = -1; + ogg->curidx = ost->curidx; + ogg->nstreams = ost->nstreams; + ogg->streams = av_realloc(ogg->streams, + ogg->nstreams * sizeof(*ogg->streams)); + + if (ogg->streams) { + memcpy(ogg->streams, ost->streams, + ost->nstreams * sizeof(*ogg->streams)); + } else { + av_free(old_streams); + ogg->nstreams = 0; + } + } + + av_free(ost); + + return 0; +} + +static int ogg_reset(AVFormatContext *s) +{ + struct ogg *ogg = s->priv_data; + int i; + int64_t start_pos = avio_tell(s->pb); + + for (i = 0; i < ogg->nstreams; i++) { + struct ogg_stream *os = ogg->streams + i; + os->bufpos = 0; + os->pstart = 0; + os->psize = 0; + os->granule = -1; + os->lastpts = AV_NOPTS_VALUE; + os->lastdts = AV_NOPTS_VALUE; + os->sync_pos = -1; + os->page_pos = 0; + os->nsegs = 0; + os->segp = 0; + os->incomplete = 0; + os->got_data = 0; + if (start_pos <= s->data_offset) { + os->lastpts = 0; + } + } + + ogg->page_pos = -1; + ogg->curidx = -1; + + return 0; +} + +static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size) +{ + int i; + + for (i = 0; ogg_codecs[i]; i++) + if (size >= ogg_codecs[i]->magicsize && + !memcmp(buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize)) + return ogg_codecs[i]; + + return NULL; +} + +/** + * Replace the current stream with a new one. This is a typical webradio + * situation where a new audio stream spawn (identified with a new serial) and + * must replace the previous one (track switch). + */ +static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, int nsegs) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os; + const struct ogg_codec *codec; + int i = 0; + + if (s->pb->seekable) { + uint8_t magic[8]; + int64_t pos = avio_tell(s->pb); + avio_skip(s->pb, nsegs); + avio_read(s->pb, magic, sizeof(magic)); + avio_seek(s->pb, pos, SEEK_SET); + codec = ogg_find_codec(magic, sizeof(magic)); + if (!codec) { + av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n"); + return AVERROR_INVALIDDATA; + } + for (i = 0; i < ogg->nstreams; i++) { + if (ogg->streams[i].codec == codec) + break; + } + if (i >= ogg->nstreams) + return ogg_new_stream(s, serial); + } else if (ogg->nstreams != 1) { + avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg"); + return AVERROR_PATCHWELCOME; + } + + os = &ogg->streams[i]; + + os->serial = serial; + return i; + +#if 0 + buf = os->buf; + bufsize = os->bufsize; + codec = os->codec; + + if (!ogg->state || ogg->state->streams[i].private != os->private) + av_freep(&ogg->streams[i].private); + + /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We + * also re-use the ogg_stream allocated buffer */ + memset(os, 0, sizeof(*os)); + os->serial = serial; + os->bufsize = bufsize; + os->buf = buf; + os->header = -1; + os->codec = codec; + + return i; +#endif +} + +static int ogg_new_stream(AVFormatContext *s, uint32_t serial) +{ + struct ogg *ogg = s->priv_data; + int idx = ogg->nstreams; + AVStream *st; + struct ogg_stream *os; + size_t size; + + if (ogg->state) { + av_log(s, AV_LOG_ERROR, "New streams are not supposed to be added " + "in between Ogg context save/restore operations.\n"); + return AVERROR_BUG; + } + + /* Allocate and init a new Ogg Stream */ + if (av_size_mult(ogg->nstreams + 1, sizeof(*ogg->streams), &size) < 0 || + !(os = av_realloc(ogg->streams, size))) + return AVERROR(ENOMEM); + ogg->streams = os; + os = ogg->streams + idx; + memset(os, 0, sizeof(*os)); + os->serial = serial; + os->bufsize = DECODER_BUFFER_SIZE; + os->buf = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); + os->header = -1; + os->start_granule = OGG_NOGRANULE_VALUE; + if (!os->buf) + return AVERROR(ENOMEM); + + /* Create the associated AVStream */ + st = avformat_new_stream(s, NULL); + if (!st) { + av_freep(&os->buf); + return AVERROR(ENOMEM); + } + st->id = idx; + avpriv_set_pts_info(st, 64, 1, 1000000); + + ogg->nstreams++; + return idx; +} + +static int ogg_new_buf(struct ogg *ogg, int idx) +{ + struct ogg_stream *os = ogg->streams + idx; + uint8_t *nb = av_malloc(os->bufsize + FF_INPUT_BUFFER_PADDING_SIZE); + int size = os->bufpos - os->pstart; + + if (os->buf) { + memcpy(nb, os->buf + os->pstart, size); + av_free(os->buf); + } + + os->buf = nb; + os->bufpos = size; + os->pstart = 0; + + return 0; +} + +static int data_packets_seen(const struct ogg *ogg) +{ + int i; + + for (i = 0; i < ogg->nstreams; i++) + if (ogg->streams[i].got_data) + return 1; + return 0; +} + +static int ogg_read_page(AVFormatContext *s, int *sid) +{ + AVIOContext *bc = s->pb; + struct ogg *ogg = s->priv_data; + struct ogg_stream *os; + int ret, i = 0; + int flags, nsegs; + uint64_t gp; + uint32_t serial; + int size, idx; + uint8_t sync[4]; + int sp = 0; + + ret = avio_read(bc, sync, 4); + if (ret < 4) + return ret < 0 ? ret : AVERROR_EOF; + + do { + int c; + + if (sync[sp & 3] == 'O' && + sync[(sp + 1) & 3] == 'g' && + sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') + break; + + if(!i && bc->seekable && ogg->page_pos > 0) { + memset(sync, 0, 4); + avio_seek(bc, ogg->page_pos+4, SEEK_SET); + ogg->page_pos = -1; + } + + c = avio_r8(bc); + + if (url_feof(bc)) + return AVERROR_EOF; + + sync[sp++ & 3] = c; + } while (i++ < MAX_PAGE_SIZE); + + if (i >= MAX_PAGE_SIZE) { + av_log(s, AV_LOG_INFO, "cannot find sync word\n"); + return AVERROR_INVALIDDATA; + } + + if (avio_r8(bc) != 0) { /* version */ + av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n"); + return AVERROR_INVALIDDATA; + } + + flags = avio_r8(bc); + gp = avio_rl64(bc); + serial = avio_rl32(bc); + avio_skip(bc, 8); /* seq, crc */ + nsegs = avio_r8(bc); + + idx = ogg_find_stream(ogg, serial); + if (idx < 0) { + if (data_packets_seen(ogg)) + idx = ogg_replace_stream(s, serial, nsegs); + else + idx = ogg_new_stream(s, serial); + + if (idx < 0) { + av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n"); + return idx; + } + } + + os = ogg->streams + idx; + ogg->page_pos = + os->page_pos = avio_tell(bc) - 27; + + if (os->psize > 0) + ogg_new_buf(ogg, idx); + + ret = avio_read(bc, os->segments, nsegs); + if (ret < nsegs) + return ret < 0 ? ret : AVERROR_EOF; + + os->nsegs = nsegs; + os->segp = 0; + + size = 0; + for (i = 0; i < nsegs; i++) + size += os->segments[i]; + + if (!(flags & OGG_FLAG_BOS)) + os->got_data = 1; + + if (flags & OGG_FLAG_CONT || os->incomplete) { + if (!os->psize) { + // If this is the very first segment we started + // playback in the middle of a continuation packet. + // Discard it since we missed the start of it. + while (os->segp < os->nsegs) { + int seg = os->segments[os->segp++]; + os->pstart += seg; + if (seg < 255) + break; + } + os->sync_pos = os->page_pos; + } + } else { + os->psize = 0; + os->sync_pos = os->page_pos; + } + + if (os->bufsize - os->bufpos < size) { + uint8_t *nb = av_malloc((os->bufsize *= 2) + FF_INPUT_BUFFER_PADDING_SIZE); + if (!nb) + return AVERROR(ENOMEM); + memcpy(nb, os->buf, os->bufpos); + av_free(os->buf); + os->buf = nb; + } + + ret = avio_read(bc, os->buf + os->bufpos, size); + if (ret < size) + return ret < 0 ? ret : AVERROR_EOF; + + os->bufpos += size; + os->granule = gp; + os->flags = flags; + + memset(os->buf + os->bufpos, 0, FF_INPUT_BUFFER_PADDING_SIZE); + if (sid) + *sid = idx; + + return 0; +} + +/** + * @brief find the next Ogg packet + * @param *sid is set to the stream for the packet or -1 if there is + * no matching stream, in that case assume all other return + * values to be uninitialized. + * @return negative value on error or EOF. + */ +static int ogg_packet(AVFormatContext *s, int *sid, int *dstart, int *dsize, + int64_t *fpos) +{ + struct ogg *ogg = s->priv_data; + int idx, i, ret; + struct ogg_stream *os; + int complete = 0; + int segp = 0, psize = 0; + + av_dlog(s, "ogg_packet: curidx=%i\n", ogg->curidx); + if (sid) + *sid = -1; + + do { + idx = ogg->curidx; + + while (idx < 0) { + ret = ogg_read_page(s, &idx); + if (ret < 0) + return ret; + } + + os = ogg->streams + idx; + + av_dlog(s, "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n", + idx, os->pstart, os->psize, os->segp, os->nsegs); + + if (!os->codec) { + if (os->header < 0) { + os->codec = ogg_find_codec(os->buf, os->bufpos); + if (!os->codec) { + av_log(s, AV_LOG_WARNING, "Codec not found\n"); + os->header = 0; + return 0; + } + } else { + return 0; + } + } + + segp = os->segp; + psize = os->psize; + + while (os->segp < os->nsegs) { + int ss = os->segments[os->segp++]; + os->psize += ss; + if (ss < 255) { + complete = 1; + break; + } + } + + if (!complete && os->segp == os->nsegs) { + ogg->curidx = -1; + // Do not set incomplete for empty packets. + // Together with the code in ogg_read_page + // that discards all continuation of empty packets + // we would get an infinite loop. + os->incomplete = !!os->psize; + } + } while (!complete); + + + if (os->granule == -1) + av_log(s, AV_LOG_WARNING, + "Page at %"PRId64" is missing granule\n", + os->page_pos); + + ogg->curidx = idx; + os->incomplete = 0; + + if (os->header) { + os->header = os->codec->header(s, idx); + if (!os->header) { + os->segp = segp; + os->psize = psize; + + // We have reached the first non-header packet in this stream. + // Unfortunately more header packets may still follow for others, + // but if we continue with header parsing we may lose data packets. + ogg->headers = 1; + + // Update the header state for all streams and + // compute the data_offset. + if (!s->data_offset) + s->data_offset = os->sync_pos; + + for (i = 0; i < ogg->nstreams; i++) { + struct ogg_stream *cur_os = ogg->streams + i; + + // if we have a partial non-header packet, its start is + // obviously at or after the data start + if (cur_os->incomplete) + s->data_offset = FFMIN(s->data_offset, cur_os->sync_pos); + } + } else { + os->nb_header++; + os->pstart += os->psize; + os->psize = 0; + } + } else { + os->pflags = 0; + os->pduration = 0; + if (os->codec && os->codec->packet) + os->codec->packet(s, idx); + if (sid) + *sid = idx; + if (dstart) + *dstart = os->pstart; + if (dsize) + *dsize = os->psize; + if (fpos) + *fpos = os->sync_pos; + os->pstart += os->psize; + os->psize = 0; + if(os->pstart == os->bufpos) + os->bufpos = os->pstart = 0; + os->sync_pos = os->page_pos; + } + + // determine whether there are more complete packets in this page + // if not, the page's granule will apply to this packet + os->page_end = 1; + for (i = os->segp; i < os->nsegs; i++) + if (os->segments[i] < 255) { + os->page_end = 0; + break; + } + + if (os->segp == os->nsegs) + ogg->curidx = -1; + + return 0; +} + +static int ogg_get_length(AVFormatContext *s) +{ + struct ogg *ogg = s->priv_data; + int i; + int64_t size, end; + int streams_left=0; + + if (!s->pb->seekable) + return 0; + +// already set + if (s->duration != AV_NOPTS_VALUE) + return 0; + + size = avio_size(s->pb); + if (size < 0) + return 0; + end = size > MAX_PAGE_SIZE ? size - MAX_PAGE_SIZE : 0; + + ogg_save(s); + avio_seek(s->pb, end, SEEK_SET); + ogg->page_pos = -1; + + while (!ogg_read_page(s, &i)) { + if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && + ogg->streams[i].codec) { + s->streams[i]->duration = + ogg_gptopts(s, i, ogg->streams[i].granule, NULL); + if (s->streams[i]->start_time != AV_NOPTS_VALUE) { + s->streams[i]->duration -= s->streams[i]->start_time; + streams_left-= (ogg->streams[i].got_start==-1); + ogg->streams[i].got_start= 1; + } else if(!ogg->streams[i].got_start) { + ogg->streams[i].got_start= -1; + streams_left++; + } + } + } + + ogg_restore(s, 0); + + ogg_save (s); + avio_seek (s->pb, s->data_offset, SEEK_SET); + ogg_reset(s); + while (streams_left > 0 && !ogg_packet(s, &i, NULL, NULL, NULL)) { + int64_t pts; + if (i < 0) continue; + pts = ogg_calc_pts(s, i, NULL); + if (pts != AV_NOPTS_VALUE && s->streams[i]->start_time == AV_NOPTS_VALUE && !ogg->streams[i].got_start) { + s->streams[i]->duration -= pts; + ogg->streams[i].got_start= 1; + streams_left--; + }else if(s->streams[i]->start_time != AV_NOPTS_VALUE && !ogg->streams[i].got_start) { + ogg->streams[i].got_start= 1; + streams_left--; + } + } + ogg_restore (s, 0); + + return 0; +} + +static int ogg_read_close(AVFormatContext *s) +{ + struct ogg *ogg = s->priv_data; + int i; + + for (i = 0; i < ogg->nstreams; i++) { + av_free(ogg->streams[i].buf); + if (ogg->streams[i].codec && + ogg->streams[i].codec->cleanup) { + ogg->streams[i].codec->cleanup(s, i); + } + av_free(ogg->streams[i].private); + } + av_free(ogg->streams); + return 0; +} + +static int ogg_read_header(AVFormatContext *s) +{ + struct ogg *ogg = s->priv_data; + int ret, i; + + ogg->curidx = -1; + + //linear headers seek from start + do { + ret = ogg_packet(s, NULL, NULL, NULL, NULL); + if (ret < 0) { + ogg_read_close(s); + return ret; + } + } while (!ogg->headers); + av_dlog(s, "found headers\n"); + + for (i = 0; i < ogg->nstreams; i++) { + struct ogg_stream *os = ogg->streams + i; + + if (ogg->streams[i].header < 0) { + av_log(s, AV_LOG_ERROR, "Header parsing failed for stream %d\n", i); + ogg->streams[i].codec = NULL; + } else if (os->codec && os->nb_header < os->codec->nb_header) { + av_log(s, AV_LOG_WARNING, "Number of headers (%d) mismatch for stream %d\n", os->nb_header, i); + } + if (os->start_granule != OGG_NOGRANULE_VALUE) + os->lastpts = s->streams[i]->start_time = + ogg_gptopts(s, i, os->start_granule, NULL); + } + + //linear granulepos seek from end + ogg_get_length(s); + + return 0; +} + +static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + int64_t pts = AV_NOPTS_VALUE; + + if (dts) + *dts = AV_NOPTS_VALUE; + + if (os->lastpts != AV_NOPTS_VALUE) { + pts = os->lastpts; + os->lastpts = AV_NOPTS_VALUE; + } + if (os->lastdts != AV_NOPTS_VALUE) { + if (dts) + *dts = os->lastdts; + os->lastdts = AV_NOPTS_VALUE; + } + if (os->page_end) { + if (os->granule != -1LL) { + if (os->codec && os->codec->granule_is_start) + pts = ogg_gptopts(s, idx, os->granule, dts); + else + os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts); + os->granule = -1LL; + } + } + return pts; +} + +static void ogg_validate_keyframe(AVFormatContext *s, int idx, int pstart, int psize) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + if (psize && s->streams[idx]->codec->codec_id == AV_CODEC_ID_THEORA) { + if (!!(os->pflags & AV_PKT_FLAG_KEY) != !(os->buf[pstart] & 0x40)) { + os->pflags ^= AV_PKT_FLAG_KEY; + av_log(s, AV_LOG_WARNING, "Broken file, %skeyframe not correctly marked.\n", + (os->pflags & AV_PKT_FLAG_KEY) ? "" : "non-"); + } + } +} + +static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + struct ogg *ogg; + struct ogg_stream *os; + int idx, ret; + int pstart, psize; + int64_t fpos, pts, dts; + + if (s->io_repositioned) { + ogg_reset(s); + s->io_repositioned = 0; + } + + //Get an ogg packet +retry: + do { + ret = ogg_packet(s, &idx, &pstart, &psize, &fpos); + if (ret < 0) + return ret; + } while (idx < 0 || !s->streams[idx]); + + ogg = s->priv_data; + os = ogg->streams + idx; + + // pflags might not be set until after this + pts = ogg_calc_pts(s, idx, &dts); + ogg_validate_keyframe(s, idx, pstart, psize); + + if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) + goto retry; + os->keyframe_seek = 0; + + //Alloc a pkt + ret = av_new_packet(pkt, psize); + if (ret < 0) + return ret; + pkt->stream_index = idx; + memcpy(pkt->data, os->buf + pstart, psize); + + pkt->pts = pts; + pkt->dts = dts; + pkt->flags = os->pflags; + pkt->duration = os->pduration; + pkt->pos = fpos; + + return psize; +} + +static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, + int64_t *pos_arg, int64_t pos_limit) +{ + struct ogg *ogg = s->priv_data; + AVIOContext *bc = s->pb; + int64_t pts = AV_NOPTS_VALUE; + int64_t keypos = -1; + int i; + int pstart, psize; + avio_seek(bc, *pos_arg, SEEK_SET); + ogg_reset(s); + + while ( avio_tell(bc) <= pos_limit + && !ogg_packet(s, &i, &pstart, &psize, pos_arg)) { + if (i == stream_index) { + struct ogg_stream *os = ogg->streams + stream_index; + pts = ogg_calc_pts(s, i, NULL); + ogg_validate_keyframe(s, i, pstart, psize); + if (os->pflags & AV_PKT_FLAG_KEY) { + keypos = *pos_arg; + } else if (os->keyframe_seek) { + // if we had a previous keyframe but no pts for it, + // return that keyframe with this pts value. + if (keypos >= 0) + *pos_arg = keypos; + else + pts = AV_NOPTS_VALUE; + } + } + if (pts != AV_NOPTS_VALUE) + break; + } + ogg_reset(s); + return pts; +} + +static int ogg_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + stream_index; + int ret; + + av_assert0(stream_index < ogg->nstreams); + // Ensure everything is reset even when seeking via + // the generated index. + ogg_reset(s); + + // Try seeking to a keyframe first. If this fails (very possible), + // av_seek_frame will fall back to ignoring keyframes + if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO + && !(flags & AVSEEK_FLAG_ANY)) + os->keyframe_seek = 1; + + ret = ff_seek_frame_binary(s, stream_index, timestamp, flags); + os = ogg->streams + stream_index; + if (ret < 0) + os->keyframe_seek = 0; + return ret; +} + +static int ogg_probe(AVProbeData *p) +{ + if (!memcmp("OggS", p->buf, 5) && p->buf[5] <= 0x7) + return AVPROBE_SCORE_MAX; + return 0; +} + +AVInputFormat ff_ogg_demuxer = { + .name = "ogg", + .long_name = NULL_IF_CONFIG_SMALL("Ogg"), + .priv_data_size = sizeof(struct ogg), + .read_probe = ogg_probe, + .read_header = ogg_read_header, + .read_packet = ogg_read_packet, + .read_close = ogg_read_close, + .read_seek = ogg_read_seek, + .read_timestamp = ogg_read_timestamp, + .extensions = "ogg", + .flags = AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/oggdec.o libavformat/oggdec.o: libavformat/oggdec.c \ + libavutil/avassert.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavformat/oggdec.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/metadata.h \ + libavformat/internal.h libavformat/vorbiscomment.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggdec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggdec.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,160 @@ +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#ifndef AVFORMAT_OGGDEC_H +#define AVFORMAT_OGGDEC_H + +#include "avformat.h" +#include "metadata.h" + +struct ogg_codec { + const int8_t *magic; + uint8_t magicsize; + const int8_t *name; + /** + * Attempt to process a packet as a header + * @return 1 if the packet was a valid header, + * 0 if the packet was not a header (was a data packet) + * -1 if an error occurred or for unsupported stream + */ + int (*header)(AVFormatContext *, int); + int (*packet)(AVFormatContext *, int); + /** + * Translate a granule into a timestamp. + * Will set dts if non-null and known. + * @return pts + */ + uint64_t (*gptopts)(AVFormatContext *, int, uint64_t, int64_t *dts); + /** + * 1 if granule is the start time of the associated packet. + * 0 if granule is the end time of the associated packet. + */ + int granule_is_start; + /** + * Number of expected headers + */ + int nb_header; + void (*cleanup)(AVFormatContext *s, int idx); +}; + +struct ogg_stream { + uint8_t *buf; + unsigned int bufsize; + unsigned int bufpos; + unsigned int pstart; + unsigned int psize; + unsigned int pflags; + unsigned int pduration; + uint32_t serial; + uint64_t granule; + uint64_t start_granule; + int64_t lastpts; + int64_t lastdts; + int64_t sync_pos; ///< file offset of the first page needed to reconstruct the current packet + int64_t page_pos; ///< file offset of the current page + int flags; + const struct ogg_codec *codec; + int header; + int nsegs, segp; + uint8_t segments[255]; + int incomplete; ///< whether we're expecting a continuation in the next page + int page_end; ///< current packet is the last one completed in the page + int keyframe_seek; + int got_start; + int got_data; ///< 1 if the stream got some data (non-initial packets), 0 otherwise + int nb_header; ///< set to the number of parsed headers + void *private; +}; + +struct ogg_state { + uint64_t pos; + int curidx; + struct ogg_state *next; + int nstreams; + struct ogg_stream streams[1]; +}; + +struct ogg { + struct ogg_stream *streams; + int nstreams; + int headers; + int curidx; + int64_t page_pos; ///< file offset of the current page + struct ogg_state *state; +}; + +#define OGG_FLAG_CONT 1 +#define OGG_FLAG_BOS 2 +#define OGG_FLAG_EOS 4 + +#define OGG_NOGRANULE_VALUE (-1ull) + +extern const struct ogg_codec ff_celt_codec; +extern const struct ogg_codec ff_dirac_codec; +extern const struct ogg_codec ff_flac_codec; +extern const struct ogg_codec ff_ogm_audio_codec; +extern const struct ogg_codec ff_ogm_old_codec; +extern const struct ogg_codec ff_ogm_text_codec; +extern const struct ogg_codec ff_ogm_video_codec; +extern const struct ogg_codec ff_old_dirac_codec; +extern const struct ogg_codec ff_old_flac_codec; +extern const struct ogg_codec ff_opus_codec; +extern const struct ogg_codec ff_skeleton_codec; +extern const struct ogg_codec ff_speex_codec; +extern const struct ogg_codec ff_theora_codec; +extern const struct ogg_codec ff_vorbis_codec; + +int ff_vorbis_comment(AVFormatContext *ms, AVDictionary **m, const uint8_t *buf, int size); + +static inline int +ogg_find_stream (struct ogg * ogg, int serial) +{ + int i; + + for (i = 0; i < ogg->nstreams; i++) + if (ogg->streams[i].serial == serial) + return i; + + return -1; +} + +static inline uint64_t +ogg_gptopts (AVFormatContext * s, int i, uint64_t gp, int64_t *dts) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + i; + uint64_t pts = AV_NOPTS_VALUE; + + if(os->codec && os->codec->gptopts){ + pts = os->codec->gptopts(s, i, gp, dts); + } else { + pts = gp; + if (dts) + *dts = pts; + } + + return pts; +} + +#endif /* AVFORMAT_OGGDEC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggdec.o Binary file ffmpeg/libavformat/oggdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,636 @@ +/* + * Ogg muxer + * Copyright (c) 2007 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/crc.h" +#include "libavutil/opt.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/random_seed.h" +#include "libavcodec/xiph.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/flac.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" +#include "vorbiscomment.h" + +#define MAX_PAGE_SIZE 65025 + +typedef struct { + int64_t start_granule; + int64_t granule; + int stream_index; + uint8_t flags; + uint8_t segments_count; + uint8_t segments[255]; + uint8_t data[MAX_PAGE_SIZE]; + uint16_t size; +} OGGPage; + +typedef struct { + unsigned page_counter; + uint8_t *header[3]; + int header_len[3]; + /** for theora granule */ + int kfgshift; + int64_t last_kf_pts; + int vrev; + int eos; + unsigned page_count; ///< number of page buffered + OGGPage page; ///< current page + unsigned serial_num; ///< serial number + int64_t last_granule; ///< last packet granule +} OGGStreamContext; + +typedef struct OGGPageList { + OGGPage page; + struct OGGPageList *next; +} OGGPageList; + +typedef struct { + const AVClass *class; + OGGPageList *page_list; + int pref_size; ///< preferred page size (0 => fill all segments) + int64_t pref_duration; ///< preferred page duration (0 => fill all segments) +} OGGContext; + +#define OFFSET(x) offsetof(OGGContext, x) +#define PARAM AV_OPT_FLAG_ENCODING_PARAM + +static const AVOption options[] = { + { "oggpagesize", "Set preferred Ogg page size.", + offsetof(OGGContext, pref_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, MAX_PAGE_SIZE, AV_OPT_FLAG_ENCODING_PARAM}, + { "pagesize", "preferred page size in bytes (deprecated)", + OFFSET(pref_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_PAGE_SIZE, PARAM }, + { "page_duration", "preferred page duration, in microseconds", + OFFSET(pref_duration), AV_OPT_TYPE_INT64, { .i64 = 1000000 }, 0, INT64_MAX, PARAM }, + { NULL }, +}; + +static const AVClass ogg_muxer_class = { + .class_name = "Ogg muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + + +static void ogg_update_checksum(AVFormatContext *s, AVIOContext *pb, int64_t crc_offset) +{ + int64_t pos = avio_tell(pb); + uint32_t checksum = ffio_get_checksum(pb); + avio_seek(pb, crc_offset, SEEK_SET); + avio_wb32(pb, checksum); + avio_seek(pb, pos, SEEK_SET); +} + +static int ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) +{ + OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data; + AVIOContext *pb; + int64_t crc_offset; + int ret, size; + uint8_t *buf; + + ret = avio_open_dyn_buf(&pb); + if (ret < 0) + return ret; + ffio_init_checksum(pb, ff_crc04C11DB7_update, 0); + ffio_wfourcc(pb, "OggS"); + avio_w8(pb, 0); + avio_w8(pb, page->flags | extra_flags); + avio_wl64(pb, page->granule); + avio_wl32(pb, oggstream->serial_num); + avio_wl32(pb, oggstream->page_counter++); + crc_offset = avio_tell(pb); + avio_wl32(pb, 0); // crc + avio_w8(pb, page->segments_count); + avio_write(pb, page->segments, page->segments_count); + avio_write(pb, page->data, page->size); + + ogg_update_checksum(s, pb, crc_offset); + avio_flush(pb); + + size = avio_close_dyn_buf(pb, &buf); + if (size < 0) + return size; + + avio_write(s->pb, buf, size); + avio_flush(s->pb); + av_free(buf); + oggstream->page_count--; + return 0; +} + +static int ogg_key_granule(OGGStreamContext *oggstream, int64_t granule) +{ + return oggstream->kfgshift && !(granule & ((1<kfgshift)-1)); +} + +static int64_t ogg_granule_to_timestamp(OGGStreamContext *oggstream, int64_t granule) +{ + if (oggstream->kfgshift) + return (granule>>oggstream->kfgshift) + + (granule & ((1<kfgshift)-1)); + else + return granule; +} + +static int ogg_compare_granule(AVFormatContext *s, OGGPage *next, OGGPage *page) +{ + AVStream *st2 = s->streams[next->stream_index]; + AVStream *st = s->streams[page->stream_index]; + int64_t next_granule, cur_granule; + + if (next->granule == -1 || page->granule == -1) + return 0; + + next_granule = av_rescale_q(ogg_granule_to_timestamp(st2->priv_data, next->granule), + st2->time_base, AV_TIME_BASE_Q); + cur_granule = av_rescale_q(ogg_granule_to_timestamp(st->priv_data, page->granule), + st ->time_base, AV_TIME_BASE_Q); + return next_granule > cur_granule; +} + +static int ogg_reset_cur_page(OGGStreamContext *oggstream) +{ + oggstream->page.granule = -1; + oggstream->page.flags = 0; + oggstream->page.segments_count = 0; + oggstream->page.size = 0; + return 0; +} + +static int ogg_buffer_page(AVFormatContext *s, OGGStreamContext *oggstream) +{ + OGGContext *ogg = s->priv_data; + OGGPageList **p = &ogg->page_list; + OGGPageList *l = av_mallocz(sizeof(*l)); + + if (!l) + return AVERROR(ENOMEM); + l->page = oggstream->page; + + oggstream->page.start_granule = oggstream->page.granule; + oggstream->page_count++; + ogg_reset_cur_page(oggstream); + + while (*p) { + if (ogg_compare_granule(s, &(*p)->page, &l->page)) + break; + p = &(*p)->next; + } + l->next = *p; + *p = l; + + return 0; +} + +static int ogg_buffer_data(AVFormatContext *s, AVStream *st, + uint8_t *data, unsigned size, int64_t granule, + int header) +{ + OGGStreamContext *oggstream = st->priv_data; + OGGContext *ogg = s->priv_data; + int total_segments = size / 255 + 1; + uint8_t *p = data; + int i, segments, len, flush = 0; + + // Handles VFR by flushing page because this frame needs to have a timestamp + // For theora, keyframes also need to have a timestamp to correctly mark + // them as such, otherwise seeking will not work correctly at the very + // least with old libogg versions. + // Do not try to flush header packets though, that will create broken files. + if (st->codec->codec_id == AV_CODEC_ID_THEORA && !header && + (ogg_granule_to_timestamp(oggstream, granule) > + ogg_granule_to_timestamp(oggstream, oggstream->last_granule) + 1 || + ogg_key_granule(oggstream, granule))) { + if (oggstream->page.granule != -1) + ogg_buffer_page(s, oggstream); + flush = 1; + } + + // avoid a continued page + if (!header && oggstream->page.size > 0 && + MAX_PAGE_SIZE - oggstream->page.size < size) { + ogg_buffer_page(s, oggstream); + } + + for (i = 0; i < total_segments; ) { + OGGPage *page = &oggstream->page; + + segments = FFMIN(total_segments - i, 255 - page->segments_count); + + if (i && !page->segments_count) + page->flags |= 1; // continued packet + + memset(page->segments+page->segments_count, 255, segments - 1); + page->segments_count += segments - 1; + + len = FFMIN(size, segments*255); + page->segments[page->segments_count++] = len - (segments-1)*255; + memcpy(page->data+page->size, p, len); + p += len; + size -= len; + i += segments; + page->size += len; + + if (i == total_segments) + page->granule = granule; + + if (!header) { + AVStream *st = s->streams[page->stream_index]; + + int64_t start = av_rescale_q(page->start_granule, st->time_base, + AV_TIME_BASE_Q); + int64_t next = av_rescale_q(page->granule, st->time_base, + AV_TIME_BASE_Q); + + if (page->segments_count == 255 || + (ogg->pref_size > 0 && page->size >= ogg->pref_size) || + (ogg->pref_duration > 0 && next - start >= ogg->pref_duration)) { + ogg_buffer_page(s, oggstream); + } + } + } + + if (flush && oggstream->page.granule != -1) + ogg_buffer_page(s, oggstream); + + return 0; +} + +static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact, + int *header_len, AVDictionary **m, int framing_bit) +{ + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; + int size; + uint8_t *p, *p0; + unsigned int count; + + ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL); + + size = offset + ff_vorbiscomment_length(*m, vendor, &count) + framing_bit; + p = av_mallocz(size); + if (!p) + return NULL; + p0 = p; + + p += offset; + ff_vorbiscomment_write(&p, m, vendor, count); + if (framing_bit) + bytestream_put_byte(&p, 1); + + *header_len = size; + return p0; +} + +static int ogg_build_flac_headers(AVCodecContext *avctx, + OGGStreamContext *oggstream, int bitexact, + AVDictionary **m) +{ + enum FLACExtradataFormat format; + uint8_t *streaminfo; + uint8_t *p; + + if (!avpriv_flac_is_extradata_valid(avctx, &format, &streaminfo)) + return -1; + + // first packet: STREAMINFO + oggstream->header_len[0] = 51; + oggstream->header[0] = av_mallocz(51); // per ogg flac specs + p = oggstream->header[0]; + if (!p) + return AVERROR(ENOMEM); + bytestream_put_byte(&p, 0x7F); + bytestream_put_buffer(&p, "FLAC", 4); + bytestream_put_byte(&p, 1); // major version + bytestream_put_byte(&p, 0); // minor version + bytestream_put_be16(&p, 1); // headers packets without this one + bytestream_put_buffer(&p, "fLaC", 4); + bytestream_put_byte(&p, 0x00); // streaminfo + bytestream_put_be24(&p, 34); + bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE); + + // second packet: VorbisComment + p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1], m, 0); + if (!p) + return AVERROR(ENOMEM); + oggstream->header[1] = p; + bytestream_put_byte(&p, 0x84); // last metadata block and vorbis comment + bytestream_put_be24(&p, oggstream->header_len[1] - 4); + + return 0; +} + +#define SPEEX_HEADER_SIZE 80 + +static int ogg_build_speex_headers(AVCodecContext *avctx, + OGGStreamContext *oggstream, int bitexact, + AVDictionary **m) +{ + uint8_t *p; + + if (avctx->extradata_size < SPEEX_HEADER_SIZE) + return -1; + + // first packet: Speex header + p = av_mallocz(SPEEX_HEADER_SIZE); + if (!p) + return AVERROR(ENOMEM); + oggstream->header[0] = p; + oggstream->header_len[0] = SPEEX_HEADER_SIZE; + bytestream_put_buffer(&p, avctx->extradata, SPEEX_HEADER_SIZE); + AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0 + + // second packet: VorbisComment + p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, 0); + if (!p) + return AVERROR(ENOMEM); + oggstream->header[1] = p; + + return 0; +} + +#define OPUS_HEADER_SIZE 19 + +static int ogg_build_opus_headers(AVCodecContext *avctx, + OGGStreamContext *oggstream, int bitexact, + AVDictionary **m) +{ + uint8_t *p; + + if (avctx->extradata_size < OPUS_HEADER_SIZE) + return -1; + + /* first packet: Opus header */ + p = av_mallocz(avctx->extradata_size); + if (!p) + return AVERROR(ENOMEM); + oggstream->header[0] = p; + oggstream->header_len[0] = avctx->extradata_size; + bytestream_put_buffer(&p, avctx->extradata, avctx->extradata_size); + + /* second packet: VorbisComment */ + p = ogg_write_vorbiscomment(8, bitexact, &oggstream->header_len[1], m, 0); + if (!p) + return AVERROR(ENOMEM); + oggstream->header[1] = p; + bytestream_put_buffer(&p, "OpusTags", 8); + + return 0; +} + +static int ogg_write_header(AVFormatContext *s) +{ + OGGContext *ogg = s->priv_data; + OGGStreamContext *oggstream = NULL; + int i, j; + + if (ogg->pref_size) + av_log(s, AV_LOG_WARNING, "The pagesize option is deprecated\n"); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + unsigned serial_num = i; + + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + if (st->codec->codec_id == AV_CODEC_ID_OPUS) + /* Opus requires a fixed 48kHz clock */ + avpriv_set_pts_info(st, 64, 1, 48000); + else + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + avpriv_set_pts_info(st, 64, st->codec->time_base.num, st->codec->time_base.den); + if (st->codec->codec_id != AV_CODEC_ID_VORBIS && + st->codec->codec_id != AV_CODEC_ID_THEORA && + st->codec->codec_id != AV_CODEC_ID_SPEEX && + st->codec->codec_id != AV_CODEC_ID_FLAC && + st->codec->codec_id != AV_CODEC_ID_OPUS) { + av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", i); + return -1; + } + + if (!st->codec->extradata || !st->codec->extradata_size) { + av_log(s, AV_LOG_ERROR, "No extradata present\n"); + return -1; + } + oggstream = av_mallocz(sizeof(*oggstream)); + oggstream->page.stream_index = i; + + if (!(st->codec->flags & CODEC_FLAG_BITEXACT)) + do { + serial_num = av_get_random_seed(); + for (j = 0; j < i; j++) { + OGGStreamContext *sc = s->streams[j]->priv_data; + if (serial_num == sc->serial_num) + break; + } + } while (j < i); + oggstream->serial_num = serial_num; + + st->priv_data = oggstream; + if (st->codec->codec_id == AV_CODEC_ID_FLAC) { + int err = ogg_build_flac_headers(st->codec, oggstream, + st->codec->flags & CODEC_FLAG_BITEXACT, + &s->metadata); + if (err) { + av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); + av_freep(&st->priv_data); + return err; + } + } else if (st->codec->codec_id == AV_CODEC_ID_SPEEX) { + int err = ogg_build_speex_headers(st->codec, oggstream, + st->codec->flags & CODEC_FLAG_BITEXACT, + &s->metadata); + if (err) { + av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); + av_freep(&st->priv_data); + return err; + } + } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) { + int err = ogg_build_opus_headers(st->codec, oggstream, + st->codec->flags & CODEC_FLAG_BITEXACT, + &s->metadata); + if (err) { + av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); + av_freep(&st->priv_data); + return err; + } + } else { + uint8_t *p; + const char *cstr = st->codec->codec_id == AV_CODEC_ID_VORBIS ? "vorbis" : "theora"; + int header_type = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 3 : 0x81; + int framing_bit = st->codec->codec_id == AV_CODEC_ID_VORBIS ? 1 : 0; + + if (avpriv_split_xiph_headers(st->codec->extradata, st->codec->extradata_size, + st->codec->codec_id == AV_CODEC_ID_VORBIS ? 30 : 42, + oggstream->header, oggstream->header_len) < 0) { + av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); + av_freep(&st->priv_data); + return -1; + } + + p = ogg_write_vorbiscomment(7, st->codec->flags & CODEC_FLAG_BITEXACT, + &oggstream->header_len[1], &s->metadata, + framing_bit); + oggstream->header[1] = p; + if (!p) + return AVERROR(ENOMEM); + + bytestream_put_byte(&p, header_type); + bytestream_put_buffer(&p, cstr, 6); + + if (st->codec->codec_id == AV_CODEC_ID_THEORA) { + /** KFGSHIFT is the width of the less significant section of the granule position + The less significant section is the frame count since the last keyframe */ + oggstream->kfgshift = ((oggstream->header[0][40]&3)<<3)|(oggstream->header[0][41]>>5); + oggstream->vrev = oggstream->header[0][9]; + av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n", + oggstream->kfgshift, oggstream->vrev); + } + } + } + + for (j = 0; j < s->nb_streams; j++) { + OGGStreamContext *oggstream = s->streams[j]->priv_data; + ogg_buffer_data(s, s->streams[j], oggstream->header[0], + oggstream->header_len[0], 0, 1); + oggstream->page.flags |= 2; // bos + ogg_buffer_page(s, oggstream); + } + for (j = 0; j < s->nb_streams; j++) { + AVStream *st = s->streams[j]; + OGGStreamContext *oggstream = st->priv_data; + for (i = 1; i < 3; i++) { + if (oggstream->header_len[i]) + ogg_buffer_data(s, st, oggstream->header[i], + oggstream->header_len[i], 0, 1); + } + ogg_buffer_page(s, oggstream); + } + + oggstream->page.start_granule = AV_NOPTS_VALUE; + + return 0; +} + +static void ogg_write_pages(AVFormatContext *s, int flush) +{ + OGGContext *ogg = s->priv_data; + OGGPageList *next, *p; + + if (!ogg->page_list) + return; + + for (p = ogg->page_list; p; ) { + OGGStreamContext *oggstream = + s->streams[p->page.stream_index]->priv_data; + if (oggstream->page_count < 2 && !flush) + break; + ogg_write_page(s, &p->page, + flush && oggstream->page_count == 1 ? 4 : 0); // eos + next = p->next; + av_freep(&p); + p = next; + } + ogg->page_list = p; +} + +static int ogg_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + OGGStreamContext *oggstream = st->priv_data; + int ret; + int64_t granule; + + if (st->codec->codec_id == AV_CODEC_ID_THEORA) { + int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts + pkt->duration; + int pframe_count; + if (pkt->flags & AV_PKT_FLAG_KEY) + oggstream->last_kf_pts = pts; + pframe_count = pts - oggstream->last_kf_pts; + // prevent frame count from overflow if key frame flag is not set + if (pframe_count >= (1<kfgshift)) { + oggstream->last_kf_pts += pframe_count; + pframe_count = 0; + } + granule = (oggstream->last_kf_pts<kfgshift) | pframe_count; + } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) + granule = pkt->pts + pkt->duration + av_rescale_q(st->codec->delay, (AVRational){ 1, st->codec->sample_rate }, st->time_base); + else + granule = pkt->pts + pkt->duration; + + if (oggstream->page.start_granule == AV_NOPTS_VALUE) + oggstream->page.start_granule = pkt->pts; + + ret = ogg_buffer_data(s, st, pkt->data, pkt->size, granule, 0); + if (ret < 0) + return ret; + + ogg_write_pages(s, 0); + + oggstream->last_granule = granule; + + return 0; +} + +static int ogg_write_trailer(AVFormatContext *s) +{ + int i; + + /* flush current page if needed */ + for (i = 0; i < s->nb_streams; i++) { + OGGStreamContext *oggstream = s->streams[i]->priv_data; + + if (oggstream->page.size > 0) + ogg_buffer_page(s, oggstream); + } + + ogg_write_pages(s, 1); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + OGGStreamContext *oggstream = st->priv_data; + if (st->codec->codec_id == AV_CODEC_ID_FLAC || + st->codec->codec_id == AV_CODEC_ID_SPEEX || + st->codec->codec_id == AV_CODEC_ID_OPUS) { + av_freep(&oggstream->header[0]); + } + av_freep(&oggstream->header[1]); + av_freep(&st->priv_data); + } + return 0; +} + +AVOutputFormat ff_ogg_muxer = { + .name = "ogg", + .long_name = NULL_IF_CONFIG_SMALL("Ogg"), + .mime_type = "application/ogg", + .extensions = "ogg,ogv,spx,opus", + .priv_data_size = sizeof(OGGContext), + .audio_codec = AV_CODEC_ID_FLAC, + .video_codec = AV_CODEC_ID_THEORA, + .write_header = ogg_write_header, + .write_packet = ogg_write_packet, + .write_trailer = ogg_write_trailer, + .priv_class = &ogg_muxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/oggenc.o libavformat/oggenc.o: libavformat/oggenc.c \ + libavutil/crc.h libavutil/attributes.h libavutil/opt.h \ + libavutil/rational.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/samplefmt.h libavutil/mathematics.h libavutil/random_seed.h \ + libavcodec/xiph.h libavutil/common.h libavcodec/bytestream.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavcodec/flac.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavcodec/get_bits.h \ + libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/internal.h libavformat/vorbiscomment.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggenc.o Binary file ffmpeg/libavformat/oggenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsecelt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsecelt.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,97 @@ +/* + * Xiph CELT parser for Ogg + * Copyright (c) 2011 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct oggcelt_private { + int extra_headers_left; +}; + +static int celt_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + struct oggcelt_private *priv = os->private; + uint8_t *p = os->buf + os->pstart; + + if (os->psize == 60 && + !memcmp(p, ff_celt_codec.magic, ff_celt_codec.magicsize)) { + /* Main header */ + + uint32_t version, sample_rate, nb_channels, frame_size; + uint32_t overlap, extra_headers; + uint8_t *extradata; + + extradata = av_malloc(2 * sizeof(uint32_t) + + FF_INPUT_BUFFER_PADDING_SIZE); + priv = av_malloc(sizeof(struct oggcelt_private)); + if (!extradata || !priv) { + av_free(extradata); + av_free(priv); + return AVERROR(ENOMEM); + } + version = AV_RL32(p + 28); + /* unused header size field skipped */ + sample_rate = AV_RL32(p + 36); + nb_channels = AV_RL32(p + 40); + frame_size = AV_RL32(p + 44); + overlap = AV_RL32(p + 48); + /* unused bytes per packet field skipped */ + extra_headers = AV_RL32(p + 56); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_CELT; + st->codec->sample_rate = sample_rate; + st->codec->channels = nb_channels; + st->codec->frame_size = frame_size; + av_free(st->codec->extradata); + st->codec->extradata = extradata; + st->codec->extradata_size = 2 * sizeof(uint32_t); + if (sample_rate) + avpriv_set_pts_info(st, 64, 1, sample_rate); + priv->extra_headers_left = 1 + extra_headers; + av_free(os->private); + os->private = priv; + AV_WL32(extradata + 0, overlap); + AV_WL32(extradata + 4, version); + return 1; + } else if (priv && priv->extra_headers_left) { + /* Extra headers (vorbiscomment) */ + + ff_vorbis_comment(s, &st->metadata, p, os->psize); + priv->extra_headers_left--; + return 1; + } else { + return 0; + } +} + +const struct ogg_codec ff_celt_codec = { + .magic = "CELT ", + .magicsize = 8, + .header = celt_header, + .nb_header = 2, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsecelt.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsecelt.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/oggparsecelt.o libavformat/oggparsecelt.o: \ + libavformat/oggparsecelt.c libavutil/intreadwrite.h libavutil/avconfig.h \ + libavutil/attributes.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsecelt.o Binary file ffmpeg/libavformat/oggparsecelt.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsedirac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsedirac.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2008 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/dirac.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +static int dirac_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + dirac_source_params source; + GetBitContext gb; + + // already parsed the header + if (st->codec->codec_id == AV_CODEC_ID_DIRAC) + return 0; + + init_get_bits(&gb, os->buf + os->pstart + 13, (os->psize - 13) * 8); + if (avpriv_dirac_parse_sequence_header(st->codec, &gb, &source) < 0) + return -1; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DIRAC; + // dirac in ogg always stores timestamps as though the video were interlaced + avpriv_set_pts_info(st, 64, st->codec->time_base.num, 2*st->codec->time_base.den); + return 1; +} + +// various undocument things: granule is signed (only for dirac!) +static uint64_t dirac_gptopts(AVFormatContext *s, int idx, uint64_t granule, + int64_t *dts_out) +{ + int64_t gp = granule; + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + + unsigned dist = ((gp >> 14) & 0xff00) | (gp & 0xff); + int64_t dts = (gp >> 31); + int64_t pts = dts + ((gp >> 9) & 0x1fff); + + if (!dist) + os->pflags |= AV_PKT_FLAG_KEY; + + if (dts_out) + *dts_out = dts; + + return pts; +} + +static int old_dirac_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + uint8_t *buf = os->buf + os->pstart; + + if (buf[0] != 'K') + return 0; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_DIRAC; + avpriv_set_pts_info(st, 64, AV_RB32(buf+12), AV_RB32(buf+8)); + return 1; +} + +static uint64_t old_dirac_gptopts(AVFormatContext *s, int idx, uint64_t gp, + int64_t *dts) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + uint64_t iframe = gp >> 30; + uint64_t pframe = gp & 0x3fffffff; + + if (!pframe) + os->pflags |= AV_PKT_FLAG_KEY; + + return iframe + pframe; +} + +const struct ogg_codec ff_dirac_codec = { + .magic = "BBCD\0", + .magicsize = 5, + .header = dirac_header, + .gptopts = dirac_gptopts, + .granule_is_start = 1, + .nb_header = 1, +}; + +const struct ogg_codec ff_old_dirac_codec = { + .magic = "KW-DIRAC", + .magicsize = 8, + .header = old_dirac_header, + .gptopts = old_dirac_gptopts, + .granule_is_start = 1, + .nb_header = 1, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsedirac.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsedirac.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/oggparsedirac.o libavformat/oggparsedirac.o: \ + libavformat/oggparsedirac.c libavcodec/get_bits.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h libavcodec/dirac.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavcodec/get_bits.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsedirac.o Binary file ffmpeg/libavformat/oggparsedirac.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseflac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseflac.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005 Matthieu CASTET + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "libavcodec/get_bits.h" +#include "libavcodec/flac.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +#define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F + +static int +flac_header (AVFormatContext * s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + GetBitContext gb; + FLACStreaminfo si; + int mdt; + + if (os->buf[os->pstart] == 0xff) + return 0; + + init_get_bits(&gb, os->buf + os->pstart, os->psize*8); + skip_bits1(&gb); /* metadata_last */ + mdt = get_bits(&gb, 7); + + if (mdt == OGG_FLAC_METADATA_TYPE_STREAMINFO) { + uint8_t *streaminfo_start = os->buf + os->pstart + 5 + 4 + 4 + 4; + skip_bits_long(&gb, 4*8); /* "FLAC" */ + if(get_bits(&gb, 8) != 1) /* unsupported major version */ + return -1; + skip_bits_long(&gb, 8 + 16); /* minor version + header count */ + skip_bits_long(&gb, 4*8); /* "fLaC" */ + + /* METADATA_BLOCK_HEADER */ + if (get_bits_long(&gb, 32) != FLAC_STREAMINFO_SIZE) + return -1; + + avpriv_flac_parse_streaminfo(st->codec, &si, streaminfo_start); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_FLAC; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + + st->codec->extradata = + av_malloc(FLAC_STREAMINFO_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + memcpy(st->codec->extradata, streaminfo_start, FLAC_STREAMINFO_SIZE); + st->codec->extradata_size = FLAC_STREAMINFO_SIZE; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } else if (mdt == FLAC_METADATA_TYPE_VORBIS_COMMENT) { + ff_vorbis_comment (s, &st->metadata, os->buf + os->pstart + 4, os->psize - 4); + } + + return 1; +} + +static int +old_flac_header (AVFormatContext * s, int idx) +{ + AVStream *st = s->streams[idx]; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_FLAC; + + return 0; +} + +const struct ogg_codec ff_flac_codec = { + .magic = "\177FLAC", + .magicsize = 5, + .header = flac_header, + .nb_header = 2, +}; + +const struct ogg_codec ff_old_flac_codec = { + .magic = "fLaC", + .magicsize = 4, + .header = old_flac_header, + .nb_header = 0, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseflac.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseflac.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/oggparseflac.o libavformat/oggparseflac.o: \ + libavformat/oggparseflac.c libavcodec/get_bits.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h libavcodec/flac.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavcodec/get_bits.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseflac.o Binary file ffmpeg/libavformat/oggparseflac.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseogm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseogm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,202 @@ +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#include +#include "libavutil/avassert.h" +#include "libavutil/intreadwrite.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" +#include "riff.h" + +static int +ogm_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + const uint8_t *p = os->buf + os->pstart; + uint64_t time_unit; + uint64_t spu; + uint32_t size; + + if(!(*p & 1)) + return 0; + + if(*p == 1) { + p++; + + if(*p == 'v'){ + int tag; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + p += 8; + tag = bytestream_get_le32(&p); + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag); + st->codec->codec_tag = tag; + } else if (*p == 't') { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_TEXT; + p += 12; + } else { + uint8_t acid[5]; + int cid; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + p += 8; + bytestream_get_buffer(&p, acid, 4); + acid[4] = 0; + cid = strtol(acid, NULL, 16); + st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, cid); + // our parser completely breaks AAC in Ogg + if (st->codec->codec_id != AV_CODEC_ID_AAC) + st->need_parsing = AVSTREAM_PARSE_FULL; + } + + size = bytestream_get_le32(&p); + size = FFMIN(size, os->psize); + time_unit = bytestream_get_le64(&p); + spu = bytestream_get_le64(&p); + p += 4; /* default_len */ + p += 8; /* buffersize + bits_per_sample */ + + if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO){ + st->codec->width = bytestream_get_le32(&p); + st->codec->height = bytestream_get_le32(&p); + avpriv_set_pts_info(st, 64, time_unit, spu * 10000000); + } else { + st->codec->channels = bytestream_get_le16(&p); + p += 2; /* block_align */ + st->codec->bit_rate = bytestream_get_le32(&p) * 8; + st->codec->sample_rate = time_unit ? spu * 10000000 / time_unit : 0; + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + if (size >= 56 && st->codec->codec_id == AV_CODEC_ID_AAC) { + p += 4; + size -= 4; + } + if (size > 52) { + av_assert0(FF_INPUT_BUFFER_PADDING_SIZE <= 52); + size -= 52; + st->codec->extradata_size = size; + st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + bytestream_get_buffer(&p, st->codec->extradata, size); + } + } + } else if (*p == 3) { + if (os->psize > 8) + ff_vorbis_comment(s, &st->metadata, p+7, os->psize-8); + } + + return 1; +} + +static int +ogm_dshow_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + uint8_t *p = os->buf + os->pstart; + uint32_t t; + + if(!(*p & 1)) + return 0; + if(*p != 1) + return 1; + + t = AV_RL32(p + 96); + + if(t == 0x05589f80){ + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, AV_RL32(p + 68)); + avpriv_set_pts_info(st, 64, AV_RL64(p + 164), 10000000); + st->codec->width = AV_RL32(p + 176); + st->codec->height = AV_RL32(p + 180); + } else if(t == 0x05589f81){ + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = ff_codec_get_id(ff_codec_wav_tags, AV_RL16(p + 124)); + st->codec->channels = AV_RL16(p + 126); + st->codec->sample_rate = AV_RL32(p + 128); + st->codec->bit_rate = AV_RL32(p + 132) * 8; + } + + return 1; +} + +static int +ogm_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + uint8_t *p = os->buf + os->pstart; + int lb; + + if(*p & 8) + os->pflags |= AV_PKT_FLAG_KEY; + + lb = ((*p & 2) << 1) | ((*p >> 6) & 3); + os->pstart += lb + 1; + os->psize -= lb + 1; + + while (lb--) + os->pduration += p[lb+1] << (lb*8); + + return 0; +} + +const struct ogg_codec ff_ogm_video_codec = { + .magic = "\001video", + .magicsize = 6, + .header = ogm_header, + .packet = ogm_packet, + .granule_is_start = 1, + .nb_header = 2, +}; + +const struct ogg_codec ff_ogm_audio_codec = { + .magic = "\001audio", + .magicsize = 6, + .header = ogm_header, + .packet = ogm_packet, + .granule_is_start = 1, + .nb_header = 2, +}; + +const struct ogg_codec ff_ogm_text_codec = { + .magic = "\001text", + .magicsize = 5, + .header = ogm_header, + .packet = ogm_packet, + .granule_is_start = 1, + .nb_header = 2, +}; + +const struct ogg_codec ff_ogm_old_codec = { + .magic = "\001Direct Show Samples embedded in Ogg", + .magicsize = 35, + .header = ogm_dshow_header, + .packet = ogm_packet, + .granule_is_start = 1, + .nb_header = 1, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseogm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseogm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/oggparseogm.o libavformat/oggparseogm.o: \ + libavformat/oggparseogm.c libavutil/avassert.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavcodec/get_bits.h libavutil/common.h \ + libavutil/intreadwrite.h libavutil/log.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h libavcodec/bytestream.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h libavformat/riff.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseogm.o Binary file ffmpeg/libavformat/oggparseogm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseopus.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseopus.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,135 @@ +/* + * Opus parser for Ogg + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct oggopus_private { + int need_comments; + unsigned pre_skip; + int64_t cur_dts; +}; + +#define OPUS_HEAD_SIZE 19 + +static int opus_header(AVFormatContext *avf, int idx) +{ + struct ogg *ogg = avf->priv_data; + struct ogg_stream *os = &ogg->streams[idx]; + AVStream *st = avf->streams[idx]; + struct oggopus_private *priv = os->private; + uint8_t *packet = os->buf + os->pstart; + uint8_t *extradata; + + if (!priv) { + priv = os->private = av_mallocz(sizeof(*priv)); + if (!priv) + return AVERROR(ENOMEM); + } + if (os->flags & OGG_FLAG_BOS) { + if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) + return AVERROR_INVALIDDATA; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_OPUS; + st->codec->channels = AV_RL8 (packet + 9); + priv->pre_skip = AV_RL16(packet + 10); + /*orig_sample_rate = AV_RL32(packet + 12);*/ + /*gain = AV_RL16(packet + 16);*/ + /*channel_map = AV_RL8 (packet + 18);*/ + + extradata = av_malloc(os->psize + FF_INPUT_BUFFER_PADDING_SIZE); + if (!extradata) + return AVERROR(ENOMEM); + memcpy(extradata, packet, os->psize); + st->codec->extradata = extradata; + st->codec->extradata_size = os->psize; + + st->codec->sample_rate = 48000; + avpriv_set_pts_info(st, 64, 1, 48000); + priv->need_comments = 1; + return 1; + } + + if (priv->need_comments) { + if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) + return AVERROR_INVALIDDATA; + ff_vorbis_comment(avf, &st->metadata, packet + 8, os->psize - 8); + priv->need_comments--; + return 1; + } + return 0; +} + +static int opus_packet(AVFormatContext *avf, int idx) +{ + struct ogg *ogg = avf->priv_data; + struct ogg_stream *os = &ogg->streams[idx]; + AVStream *st = avf->streams[idx]; + struct oggopus_private *priv = os->private; + uint8_t *packet = os->buf + os->pstart; + unsigned toc, toc_config, toc_count, frame_size, nb_frames = 1; + + if (!os->psize) + return AVERROR_INVALIDDATA; + toc = *packet; + toc_config = toc >> 3; + toc_count = toc & 3; + frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : + toc_config < 16 ? 480 << (toc_config & 1) : + 120 << (toc_config & 3); + if (toc_count == 3) { + if (os->psize < 2) + return AVERROR_INVALIDDATA; + nb_frames = packet[1] & 0x3F; + } else if (toc_count) { + nb_frames = 2; + } + os->pduration = frame_size * nb_frames; + if (os->lastpts != AV_NOPTS_VALUE) { + if (st->start_time == AV_NOPTS_VALUE) + st->start_time = os->lastpts; + priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; + } + priv->cur_dts += os->pduration; + if ((os->flags & OGG_FLAG_EOS)) { + int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; + skip = FFMIN(skip, os->pduration); + if (skip > 0) { + os->pduration = skip < os->pduration ? os->pduration - skip : 1; + av_log(avf, AV_LOG_WARNING, + "Last packet must be truncated to %d (unimplemented).\n", + os->pduration); + } + } + return 0; +} + +const struct ogg_codec ff_opus_codec = { + .name = "Opus", + .magic = "OpusHead", + .magicsize = 8, + .header = opus_header, + .packet = opus_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseopus.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseopus.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/oggparseopus.o libavformat/oggparseopus.o: \ + libavformat/oggparseopus.c libavutil/intreadwrite.h libavutil/avconfig.h \ + libavutil/attributes.h libavutil/bswap.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseopus.o Binary file ffmpeg/libavformat/oggparseopus.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseskeleton.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseskeleton.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +static int skeleton_header(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + uint8_t *buf = os->buf + os->pstart; + int version_major, version_minor; + int64_t start_num, start_den; + uint64_t start_granule; + int target_idx, start_time; + + strcpy(st->codec->codec_name, "skeleton"); + st->codec->codec_type = AVMEDIA_TYPE_DATA; + + if (os->psize < 8) + return -1; + + if (!strncmp(buf, "fishead", 8)) { + if (os->psize < 64) + return -1; + + version_major = AV_RL16(buf+8); + version_minor = AV_RL16(buf+10); + + if (version_major != 3 && version_major != 4) { + av_log(s, AV_LOG_WARNING, "Unknown skeleton version %d.%d\n", + version_major, version_minor); + return -1; + } + + // This is the overall start time. We use it for the start time of + // of the skeleton stream since if left unset lavf assumes 0, + // which we don't want since skeleton is timeless + // FIXME: the real meaning of this field is "start playback at + // this time which can be in the middle of a packet + start_num = AV_RL64(buf+12); + start_den = AV_RL64(buf+20); + + if (start_den > 0 && start_num > 0) { + int base_den; + av_reduce(&start_time, &base_den, start_num, start_den, INT_MAX); + avpriv_set_pts_info(st, 64, 1, base_den); + os->lastpts = + st->start_time = start_time; + } + } else if (!strncmp(buf, "fisbone", 8)) { + if (os->psize < 52) + return -1; + + target_idx = ogg_find_stream(ogg, AV_RL32(buf+12)); + start_granule = AV_RL64(buf+36); + if (os->start_granule != OGG_NOGRANULE_VALUE) { + avpriv_report_missing_feature(s, + "Multiple fisbone for the same stream"); + return 1; + } + if (target_idx >= 0 && start_granule != OGG_NOGRANULE_VALUE) { + os->start_granule = start_granule; + } + } + + return 1; +} + +const struct ogg_codec ff_skeleton_codec = { + .magic = "fishead", + .magicsize = 8, + .header = skeleton_header, + .nb_header = 0, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseskeleton.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparseskeleton.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/oggparseskeleton.o libavformat/oggparseskeleton.o: \ + libavformat/oggparseskeleton.c libavcodec/bytestream.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/common.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intreadwrite.h \ + libavutil/bswap.h libavutil/x86/bswap.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparseskeleton.o Binary file ffmpeg/libavformat/oggparseskeleton.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsespeex.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsespeex.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,139 @@ +/* + Copyright (C) 2008 Reimar Döffinger + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#include +#include "libavutil/bswap.h" +#include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct speex_params { + int packet_size; + int final_packet_duration; + int seq; +}; + +static int speex_header(AVFormatContext *s, int idx) { + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + struct speex_params *spxp = os->private; + AVStream *st = s->streams[idx]; + uint8_t *p = os->buf + os->pstart; + + if (!spxp) { + spxp = av_mallocz(sizeof(*spxp)); + os->private = spxp; + } + + if (spxp->seq > 1) + return 0; + + if (spxp->seq == 0) { + int frames_per_packet; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_SPEEX; + + if (os->psize < 68) { + av_log(s, AV_LOG_ERROR, "speex packet too small\n"); + return AVERROR_INVALIDDATA; + } + + st->codec->sample_rate = AV_RL32(p + 36); + st->codec->channels = AV_RL32(p + 48); + if (st->codec->channels < 1 || st->codec->channels > 2) { + av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n"); + return AVERROR_INVALIDDATA; + } + st->codec->channel_layout = st->codec->channels == 1 ? AV_CH_LAYOUT_MONO : + AV_CH_LAYOUT_STEREO; + + spxp->packet_size = AV_RL32(p + 56); + frames_per_packet = AV_RL32(p + 64); + if (frames_per_packet) + spxp->packet_size *= frames_per_packet; + + st->codec->extradata_size = os->psize; + st->codec->extradata = av_malloc(st->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + memcpy(st->codec->extradata, p, st->codec->extradata_size); + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + } else + ff_vorbis_comment(s, &st->metadata, p, os->psize); + + spxp->seq++; + return 1; +} + +static int ogg_page_packets(struct ogg_stream *os) +{ + int i; + int packets = 0; + for (i = 0; i < os->nsegs; i++) + if (os->segments[i] < 255) + packets++; + return packets; +} + +static int speex_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + struct speex_params *spxp = os->private; + int packet_size = spxp->packet_size; + + if (os->flags & OGG_FLAG_EOS && os->lastpts != AV_NOPTS_VALUE && + os->granule > 0) { + /* first packet of final page. we have to calculate the final packet + duration here because it is the only place we know the next-to-last + granule position. */ + spxp->final_packet_duration = os->granule - os->lastpts - + packet_size * (ogg_page_packets(os) - 1); + } + + if (!os->lastpts && os->granule > 0) + /* first packet */ + os->lastpts = os->lastdts = os->granule - packet_size * + ogg_page_packets(os); + if (os->flags & OGG_FLAG_EOS && os->segp == os->nsegs && + spxp->final_packet_duration) + /* final packet */ + os->pduration = spxp->final_packet_duration; + else + os->pduration = packet_size; + + return 0; +} + +const struct ogg_codec ff_speex_codec = { + .magic = "Speex ", + .magicsize = 8, + .header = speex_header, + .packet = speex_packet, + .nb_header = 2, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsespeex.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsespeex.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +libavformat/oggparsespeex.o libavformat/oggparsespeex.o: \ + libavformat/oggparsespeex.c libavutil/bswap.h libavutil/avconfig.h \ + libavutil/attributes.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/avstring.h libavutil/channel_layout.h \ + libavcodec/get_bits.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/common.h libavutil/mem.h \ + libavutil/error.h libavutil/avutil.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/bytestream.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/oggdec.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsespeex.o Binary file ffmpeg/libavformat/oggparsespeex.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsetheora.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsetheora.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,206 @@ +/** + Copyright (C) 2005 Matthieu CASTET, Alex Beregszaszi + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#include +#include "libavutil/bswap.h" +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" + +struct theora_params { + int gpshift; + int gpmask; + unsigned version; +}; + +static int +theora_header (AVFormatContext * s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + struct theora_params *thp = os->private; + int cds = st->codec->extradata_size + os->psize + 2; + uint8_t *cdp; + + if(!(os->buf[os->pstart] & 0x80)) + return 0; + + if(!thp){ + thp = av_mallocz(sizeof(*thp)); + os->private = thp; + } + + switch (os->buf[os->pstart]) { + case 0x80: { + GetBitContext gb; + int width, height; + AVRational timebase; + + init_get_bits(&gb, os->buf + os->pstart, os->psize*8); + + skip_bits_long(&gb, 7*8); /* 0x80"theora" */ + + thp->version = get_bits_long(&gb, 24); + if (thp->version < 0x030100) + { + av_log(s, AV_LOG_ERROR, + "Too old or unsupported Theora (%x)\n", thp->version); + return -1; + } + + width = get_bits(&gb, 16) << 4; + height = get_bits(&gb, 16) << 4; + avcodec_set_dimensions(st->codec, width, height); + + if (thp->version >= 0x030400) + skip_bits(&gb, 100); + + if (thp->version >= 0x030200) { + width = get_bits_long(&gb, 24); + height = get_bits_long(&gb, 24); + if ( width <= st->codec->width && width > st->codec->width-16 + && height <= st->codec->height && height > st->codec->height-16) + avcodec_set_dimensions(st->codec, width, height); + + skip_bits(&gb, 16); + } + timebase.den = get_bits_long(&gb, 32); + timebase.num = get_bits_long(&gb, 32); + if (!(timebase.num > 0 && timebase.den > 0)) { + av_log(s, AV_LOG_WARNING, "Invalid time base in theora stream, assuming 25 FPS\n"); + timebase.num = 1; + timebase.den = 25; + } + avpriv_set_pts_info(st, 64, timebase.num, timebase.den); + + st->sample_aspect_ratio.num = get_bits_long(&gb, 24); + st->sample_aspect_ratio.den = get_bits_long(&gb, 24); + + if (thp->version >= 0x030200) + skip_bits_long(&gb, 38); + if (thp->version >= 0x304000) + skip_bits(&gb, 2); + + thp->gpshift = get_bits(&gb, 5); + thp->gpmask = (1 << thp->gpshift) - 1; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_THEORA; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + } + break; + case 0x81: + ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7, os->psize - 7); + case 0x82: + if (!thp->version) + return -1; + break; + default: + av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); + return -1; + } + + st->codec->extradata = av_realloc (st->codec->extradata, + cds + FF_INPUT_BUFFER_PADDING_SIZE); + cdp = st->codec->extradata + st->codec->extradata_size; + *cdp++ = os->psize >> 8; + *cdp++ = os->psize & 0xff; + memcpy (cdp, os->buf + os->pstart, os->psize); + st->codec->extradata_size = cds; + + return 1; +} + +static uint64_t +theora_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, int64_t *dts) +{ + struct ogg *ogg = ctx->priv_data; + struct ogg_stream *os = ogg->streams + idx; + struct theora_params *thp = os->private; + uint64_t iframe, pframe; + + if (!thp) + return AV_NOPTS_VALUE; + + iframe = gp >> thp->gpshift; + pframe = gp & thp->gpmask; + + if (thp->version < 0x030201) + iframe++; + + if(!pframe) + os->pflags |= AV_PKT_FLAG_KEY; + + if (dts) + *dts = iframe + pframe; + + return iframe + pframe; +} + +static int theora_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + int duration; + + /* first packet handling + here we parse the duration of each packet in the first page and compare + the total duration to the page granule to find the encoder delay and + set the first timestamp */ + + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg; + + duration = 1; + for (seg = os->segp; seg < os->nsegs; seg++) { + if (os->segments[seg] < 255) + duration ++; + } + + os->lastpts = os->lastdts = theora_gptopts(s, idx, os->granule, NULL) - duration; + if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = os->lastpts; + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } + } + + /* parse packet duration */ + if (os->psize > 0) { + os->pduration = 1; + } + + return 0; +} + +const struct ogg_codec ff_theora_codec = { + .magic = "\200theora", + .magicsize = 7, + .header = theora_header, + .packet = theora_packet, + .gptopts = theora_gptopts, + .nb_header = 3, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsetheora.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsetheora.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,23 @@ +libavformat/oggparsetheora.o libavformat/oggparsetheora.o: \ + libavformat/oggparsetheora.c libavutil/bswap.h libavutil/avconfig.h \ + libavutil/attributes.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavcodec/get_bits.h libavutil/common.h \ + libavutil/version.h libavutil/intmath.h libavutil/common.h \ + libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/dict.h libavutil/log.h \ + libavformat/avio.h libavutil/common.h libavformat/version.h \ + libavutil/avutil.h libavformat/internal.h libavformat/oggdec.h \ + libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsetheora.o Binary file ffmpeg/libavformat/oggparsetheora.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsevorbis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsevorbis.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,392 @@ +/** + Copyright (C) 2005 Michael Ahlberg, Måns Rullgård + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +**/ + +#include +#include "libavutil/avstring.h" +#include "libavutil/bswap.h" +#include "libavutil/dict.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/vorbis_parser.h" +#include "avformat.h" +#include "internal.h" +#include "oggdec.h" +#include "vorbiscomment.h" + +static int ogm_chapter(AVFormatContext *as, uint8_t *key, uint8_t *val) +{ + int i, cnum, h, m, s, ms, keylen = strlen(key); + AVChapter *chapter = NULL; + + if (keylen < 9 || sscanf(key, "CHAPTER%02d", &cnum) != 1) + return 0; + + if (keylen == 9) { + if (sscanf(val, "%02d:%02d:%02d.%03d", &h, &m, &s, &ms) < 4) + return 0; + + avpriv_new_chapter(as, cnum, (AVRational){1,1000}, + ms + 1000*(s + 60*(m + 60*h)), + AV_NOPTS_VALUE, NULL); + av_free(val); + } else if (!strcmp(key+9, "NAME")) { + for(i = 0; i < as->nb_chapters; i++) + if (as->chapters[i]->id == cnum) { + chapter = as->chapters[i]; + break; + } + if (!chapter) + return 0; + + av_dict_set(&chapter->metadata, "title", val, + AV_DICT_DONT_STRDUP_VAL); + } else + return 0; + + av_free(key); + return 1; +} + +int +ff_vorbis_comment(AVFormatContext * as, AVDictionary **m, const uint8_t *buf, int size) +{ + const uint8_t *p = buf; + const uint8_t *end = buf + size; + unsigned n, j; + int s; + + if (size < 8) /* must have vendor_length and user_comment_list_length */ + return -1; + + s = bytestream_get_le32(&p); + + if (end - p - 4 < s || s < 0) + return -1; + + p += s; + + n = bytestream_get_le32(&p); + + while (end - p >= 4 && n > 0) { + const char *t, *v; + int tl, vl; + + s = bytestream_get_le32(&p); + + if (end - p < s || s < 0) + break; + + t = p; + p += s; + n--; + + v = memchr(t, '=', s); + if (!v) + continue; + + tl = v - t; + vl = s - tl - 1; + v++; + + if (tl && vl) { + char *tt, *ct; + + tt = av_malloc(tl + 1); + ct = av_malloc(vl + 1); + if (!tt || !ct) { + av_freep(&tt); + av_freep(&ct); + av_log(as, AV_LOG_WARNING, "out-of-memory error. skipping VorbisComment tag.\n"); + continue; + } + + for (j = 0; j < tl; j++) + tt[j] = av_toupper(t[j]); + tt[tl] = 0; + + memcpy(ct, v, vl); + ct[vl] = 0; + + if (!ogm_chapter(as, tt, ct)) + av_dict_set(m, tt, ct, + AV_DICT_DONT_STRDUP_KEY | + AV_DICT_DONT_STRDUP_VAL); + } + } + + if (p != end) + av_log(as, AV_LOG_INFO, "%ti bytes of comment header remain\n", end-p); + if (n > 0) + av_log(as, AV_LOG_INFO, + "truncated comment header, %i comments not found\n", n); + + ff_metadata_conv(m, NULL, ff_vorbiscomment_metadata_conv); + + return 0; +} + + +/** Parse the vorbis header + * Vorbis Identification header from Vorbis_I_spec.html#vorbis-spec-codec + * [vorbis_version] = read 32 bits as unsigned integer | Not used + * [audio_channels] = read 8 bit integer as unsigned | Used + * [audio_sample_rate] = read 32 bits as unsigned integer | Used + * [bitrate_maximum] = read 32 bits as signed integer | Not used yet + * [bitrate_nominal] = read 32 bits as signed integer | Not used yet + * [bitrate_minimum] = read 32 bits as signed integer | Used as bitrate + * [blocksize_0] = read 4 bits as unsigned integer | Not Used + * [blocksize_1] = read 4 bits as unsigned integer | Not Used + * [framing_flag] = read one bit | Not Used + * */ + +struct oggvorbis_private { + unsigned int len[3]; + unsigned char *packet[3]; + VorbisParseContext vp; + int64_t final_pts; + int final_duration; +}; + + +static unsigned int +fixup_vorbis_headers(AVFormatContext * as, struct oggvorbis_private *priv, + uint8_t **buf) +{ + int i,offset, len, buf_len; + unsigned char *ptr; + + len = priv->len[0] + priv->len[1] + priv->len[2]; + buf_len = len + len/255 + 64; + ptr = *buf = av_realloc(NULL, buf_len); + if (!*buf) + return 0; + memset(*buf, '\0', buf_len); + + ptr[0] = 2; + offset = 1; + offset += av_xiphlacing(&ptr[offset], priv->len[0]); + offset += av_xiphlacing(&ptr[offset], priv->len[1]); + for (i = 0; i < 3; i++) { + memcpy(&ptr[offset], priv->packet[i], priv->len[i]); + offset += priv->len[i]; + av_freep(&priv->packet[i]); + } + *buf = av_realloc(*buf, offset + FF_INPUT_BUFFER_PADDING_SIZE); + return offset; +} + +static void vorbis_cleanup(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + struct oggvorbis_private *priv = os->private; + int i; + if (os->private) + for (i = 0; i < 3; i++) + av_freep(&priv->packet[i]); +} + +static int +vorbis_header (AVFormatContext * s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + AVStream *st = s->streams[idx]; + struct oggvorbis_private *priv; + int pkt_type = os->buf[os->pstart]; + + if (!(pkt_type & 1)) + return os->private ? 0 : -1; + + if (!os->private) { + os->private = av_mallocz(sizeof(struct oggvorbis_private)); + if (!os->private) + return -1; + } + + if (os->psize < 1 || pkt_type > 5) + return -1; + + priv = os->private; + + if (priv->packet[pkt_type>>1]) + return -1; + if (pkt_type > 1 && !priv->packet[0] || pkt_type > 3 && !priv->packet[1]) + return -1; + + priv->len[pkt_type >> 1] = os->psize; + priv->packet[pkt_type >> 1] = av_mallocz(os->psize); + if (!priv->packet[pkt_type >> 1]) + return AVERROR(ENOMEM); + memcpy(priv->packet[pkt_type >> 1], os->buf + os->pstart, os->psize); + if (os->buf[os->pstart] == 1) { + const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */ + unsigned blocksize, bs0, bs1; + int srate; + int channels; + + if (os->psize != 30) + return -1; + + if (bytestream_get_le32(&p) != 0) /* vorbis_version */ + return -1; + + channels= bytestream_get_byte(&p); + if (st->codec->channels && channels != st->codec->channels) { + av_log(s, AV_LOG_ERROR, "Channel change is not supported\n"); + return AVERROR_PATCHWELCOME; + } + st->codec->channels = channels; + srate = bytestream_get_le32(&p); + p += 4; // skip maximum bitrate + st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate + p += 4; // skip minimum bitrate + + blocksize = bytestream_get_byte(&p); + bs0 = blocksize & 15; + bs1 = blocksize >> 4; + + if (bs0 > bs1) + return -1; + if (bs0 < 6 || bs1 > 13) + return -1; + + if (bytestream_get_byte(&p) != 1) /* framing_flag */ + return -1; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_VORBIS; + + if (srate > 0) { + st->codec->sample_rate = srate; + avpriv_set_pts_info(st, 64, 1, srate); + } + } else if (os->buf[os->pstart] == 3) { + if (os->psize > 8 && + ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7, os->psize - 8) >= 0) { + // drop all metadata we parsed and which is not required by libvorbis + unsigned new_len = 7 + 4 + AV_RL32(priv->packet[1] + 7) + 4 + 1; + if (new_len >= 16 && new_len < os->psize) { + AV_WL32(priv->packet[1] + new_len - 5, 0); + priv->packet[1][new_len - 1] = 1; + priv->len[1] = new_len; + } + } + } else { + int ret; + st->codec->extradata_size = + fixup_vorbis_headers(s, priv, &st->codec->extradata); + if ((ret = avpriv_vorbis_parse_extradata(st->codec, &priv->vp))) { + av_freep(&st->codec->extradata); + st->codec->extradata_size = 0; + return ret; + } + } + + return 1; +} + +static int vorbis_packet(AVFormatContext *s, int idx) +{ + struct ogg *ogg = s->priv_data; + struct ogg_stream *os = ogg->streams + idx; + struct oggvorbis_private *priv = os->private; + int duration; + + /* first packet handling + here we parse the duration of each packet in the first page and compare + the total duration to the page granule to find the encoder delay and + set the first timestamp */ + if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { + int seg, d; + uint8_t *last_pkt = os->buf + os->pstart; + uint8_t *next_pkt = last_pkt; + + avpriv_vorbis_parse_reset(&priv->vp); + duration = 0; + seg = os->segp; + d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); + if (d < 0) { + os->pflags |= AV_PKT_FLAG_CORRUPT; + return 0; + } + duration += d; + last_pkt = next_pkt = next_pkt + os->psize; + for (; seg < os->nsegs; seg++) { + if (os->segments[seg] < 255) { + int d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1); + if (d < 0) { + duration = os->granule; + break; + } + duration += d; + last_pkt = next_pkt + os->segments[seg]; + } + next_pkt += os->segments[seg]; + } + os->lastpts = os->lastdts = os->granule - duration; + if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + s->streams[idx]->start_time = FFMAX(os->lastpts, 0); + if (s->streams[idx]->duration) + s->streams[idx]->duration -= s->streams[idx]->start_time; + } + priv->final_pts = AV_NOPTS_VALUE; + avpriv_vorbis_parse_reset(&priv->vp); + } + + /* parse packet duration */ + if (os->psize > 0) { + duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1); + if (duration < 0) { + os->pflags |= AV_PKT_FLAG_CORRUPT; + return 0; + } + os->pduration = duration; + } + + /* final packet handling + here we save the pts of the first packet in the final page, sum up all + packet durations in the final page except for the last one, and compare + to the page granule to find the duration of the final packet */ + if (os->flags & OGG_FLAG_EOS) { + if (os->lastpts != AV_NOPTS_VALUE) { + priv->final_pts = os->lastpts; + priv->final_duration = 0; + } + if (os->segp == os->nsegs) + os->pduration = os->granule - priv->final_pts - priv->final_duration; + priv->final_duration += os->pduration; + } + + return 0; +} + +const struct ogg_codec ff_vorbis_codec = { + .magic = "\001vorbis", + .magicsize = 7, + .header = vorbis_header, + .packet = vorbis_packet, + .cleanup= vorbis_cleanup, + .nb_header = 3, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsevorbis.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oggparsevorbis.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,24 @@ +libavformat/oggparsevorbis.o libavformat/oggparsevorbis.o: \ + libavformat/oggparsevorbis.c libavutil/avstring.h libavutil/attributes.h \ + libavutil/bswap.h libavutil/avconfig.h config.h libavutil/x86/bswap.h \ + config.h libavutil/attributes.h libavutil/dict.h libavcodec/get_bits.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/common.h libavutil/mem.h libavutil/error.h libavutil/avutil.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/intreadwrite.h \ + libavutil/log.h libavutil/avassert.h libavcodec/mathops.h config.h \ + libavcodec/x86/mathops.h config.h libavutil/common.h \ + libavcodec/bytestream.h libavcodec/vorbis_parser.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h libavformat/oggdec.h libavformat/metadata.h \ + libavformat/vorbiscomment.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oggparsevorbis.o Binary file ffmpeg/libavformat/oggparsevorbis.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oma.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oma.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,34 @@ +/* + * Sony OpenMG (OMA) common data + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "internal.h" +#include "oma.h" +#include "libavcodec/avcodec.h" + +const uint16_t ff_oma_srate_tab[8] = { 320, 441, 480, 882, 960, 0 }; + +const AVCodecTag ff_oma_codec_tags[] = { + { AV_CODEC_ID_ATRAC3, OMA_CODECID_ATRAC3 }, + { AV_CODEC_ID_ATRAC3P, OMA_CODECID_ATRAC3P }, + { AV_CODEC_ID_MP3, OMA_CODECID_MP3 }, + { AV_CODEC_ID_PCM_S16BE, OMA_CODECID_LPCM }, + { 0 }, +}; + diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oma.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oma.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/oma.o libavformat/oma.o: libavformat/oma.c \ + libavformat/internal.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/oma.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oma.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/oma.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,44 @@ +/* + * Sony OpenMG (OMA) common data + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_OMA_H +#define AVFORMAT_OMA_H + +#include + +#include "internal.h" + +#define EA3_HEADER_SIZE 96 +#define ID3v2_EA3_MAGIC "ea3" +#define OMA_ENC_HEADER_SIZE 16 + +enum { + OMA_CODECID_ATRAC3 = 0, + OMA_CODECID_ATRAC3P = 1, + OMA_CODECID_MP3 = 3, + OMA_CODECID_LPCM = 4, + OMA_CODECID_WMA = 5, +}; + +extern const uint16_t ff_oma_srate_tab[8]; + +extern const AVCodecTag ff_oma_codec_tags[]; + +#endif /* AVFORMAT_OMA_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/oma.o Binary file ffmpeg/libavformat/oma.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omadec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/omadec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,462 @@ +/* + * Sony OpenMG (OMA) demuxer + * + * Copyright (c) 2008 Maxim Poliakovski + * 2008 Benjamin Larsson + * 2011 David Goldwich + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * This is a demuxer for Sony OpenMG Music files + * + * Known file extensions: ".oma", "aa3" + * The format of such files consists of three parts: + * - "ea3" header carrying overall info and metadata. Except for starting with + * "ea" instead of "ID", it's an ID3v2 header. + * - "EA3" header is a Sony-specific header containing information about + * the OpenMG file: codec type (usually ATRAC, can also be MP3 or WMA), + * codec specific info (packet size, sample rate, channels and so on) + * and DRM related info (file encryption, content id). + * - Sound data organized in packets follow the EA3 header + * (can be encrypted using the Sony DRM!). + * + */ + +#include "libavutil/channel_layout.h" +#include "avformat.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/des.h" +#include "oma.h" +#include "pcm.h" +#include "id3v2.h" + + +static const uint64_t leaf_table[] = { + 0xd79e8283acea4620, 0x7a9762f445afd0d8, + 0x354d60a60b8c79f1, 0x584e1cde00b07aee, + 0x1573cd93da7df623, 0x47f98d79620dd535 +}; + +typedef struct OMAContext { + uint64_t content_start; + int encrypted; + uint16_t k_size; + uint16_t e_size; + uint16_t i_size; + uint16_t s_size; + uint32_t rid; + uint8_t r_val[24]; + uint8_t n_val[24]; + uint8_t m_val[8]; + uint8_t s_val[8]; + uint8_t sm_val[8]; + uint8_t e_val[8]; + uint8_t iv[8]; + struct AVDES av_des; +} OMAContext; + +static void hex_log(AVFormatContext *s, int level, const char *name, const uint8_t *value, int len) +{ + char buf[33]; + len = FFMIN(len, 16); + if (av_log_get_level() < level) + return; + ff_data_to_hex(buf, value, len, 1); + buf[len<<1] = '\0'; + av_log(s, level, "%s: %s\n", name, buf); +} + +static int kset(AVFormatContext *s, const uint8_t *r_val, const uint8_t *n_val, int len) +{ + OMAContext *oc = s->priv_data; + + if (!r_val && !n_val) + return -1; + + len = FFMIN(len, 16); + + /* use first 64 bits in the third round again */ + if (r_val) { + if (r_val != oc->r_val) { + memset(oc->r_val, 0, 24); + memcpy(oc->r_val, r_val, len); + } + memcpy(&oc->r_val[16], r_val, 8); + } + if (n_val) { + if (n_val != oc->n_val) { + memset(oc->n_val, 0, 24); + memcpy(oc->n_val, n_val, len); + } + memcpy(&oc->n_val[16], n_val, 8); + } + + return 0; +} + +static int rprobe(AVFormatContext *s, uint8_t *enc_header, const uint8_t *r_val) +{ + OMAContext *oc = s->priv_data; + unsigned int pos; + struct AVDES av_des; + + if (!enc_header || !r_val) + return -1; + + /* m_val */ + av_des_init(&av_des, r_val, 192, 1); + av_des_crypt(&av_des, oc->m_val, &enc_header[48], 1, NULL, 1); + + /* s_val */ + av_des_init(&av_des, oc->m_val, 64, 0); + av_des_crypt(&av_des, oc->s_val, NULL, 1, NULL, 0); + + /* sm_val */ + pos = OMA_ENC_HEADER_SIZE + oc->k_size + oc->e_size; + av_des_init(&av_des, oc->s_val, 64, 0); + av_des_mac(&av_des, oc->sm_val, &enc_header[pos], (oc->i_size >> 3)); + + pos += oc->i_size; + + return memcmp(&enc_header[pos], oc->sm_val, 8) ? -1 : 0; +} + +static int nprobe(AVFormatContext *s, uint8_t *enc_header, int size, const uint8_t *n_val) +{ + OMAContext *oc = s->priv_data; + uint32_t pos, taglen, datalen; + struct AVDES av_des; + + if (!enc_header || !n_val) + return -1; + + pos = OMA_ENC_HEADER_SIZE + oc->k_size; + if (!memcmp(&enc_header[pos], "EKB ", 4)) + pos += 32; + + if (AV_RB32(&enc_header[pos]) != oc->rid) + av_log(s, AV_LOG_DEBUG, "Mismatching RID\n"); + + taglen = AV_RB32(&enc_header[pos+32]); + datalen = AV_RB32(&enc_header[pos+36]) >> 4; + + if(pos + (uint64_t)taglen + (((uint64_t)datalen)<<4) + 44 > size) + return -1; + + pos += 44 + taglen; + + av_des_init(&av_des, n_val, 192, 1); + while (datalen-- > 0) { + av_des_crypt(&av_des, oc->r_val, &enc_header[pos], 2, NULL, 1); + kset(s, oc->r_val, NULL, 16); + if (!rprobe(s, enc_header, oc->r_val)) + return 0; + pos += 16; + } + + return -1; +} + +static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) +{ + OMAContext *oc = s->priv_data; + ID3v2ExtraMetaGEOB *geob = NULL; + uint8_t *gdata; + + oc->encrypted = 1; + av_log(s, AV_LOG_INFO, "File is encrypted\n"); + + /* find GEOB metadata */ + while (em) { + if (!strcmp(em->tag, "GEOB") && + (geob = em->data) && + (!strcmp(geob->description, "OMG_LSI") || + !strcmp(geob->description, "OMG_BKLSI"))) { + break; + } + em = em->next; + } + if (!em) { + av_log(s, AV_LOG_ERROR, "No encryption header found\n"); + return -1; + } + + if (geob->datasize < 64) { + av_log(s, AV_LOG_ERROR, "Invalid GEOB data size: %u\n", geob->datasize); + return -1; + } + + gdata = geob->data; + + if (AV_RB16(gdata) != 1) + av_log(s, AV_LOG_WARNING, "Unknown version in encryption header\n"); + + oc->k_size = AV_RB16(&gdata[2]); + oc->e_size = AV_RB16(&gdata[4]); + oc->i_size = AV_RB16(&gdata[6]); + oc->s_size = AV_RB16(&gdata[8]); + + if (memcmp(&gdata[OMA_ENC_HEADER_SIZE], "KEYRING ", 12)) { + av_log(s, AV_LOG_ERROR, "Invalid encryption header\n"); + return -1; + } + if ( OMA_ENC_HEADER_SIZE + oc->k_size + oc->e_size + oc->i_size + 8 > geob->datasize + || OMA_ENC_HEADER_SIZE + 48 > geob->datasize + ) { + av_log(s, AV_LOG_ERROR, "Too little GEOB data\n"); + return AVERROR_INVALIDDATA; + } + oc->rid = AV_RB32(&gdata[OMA_ENC_HEADER_SIZE + 28]); + av_log(s, AV_LOG_DEBUG, "RID: %.8x\n", oc->rid); + + memcpy(oc->iv, &header[0x58], 8); + hex_log(s, AV_LOG_DEBUG, "IV", oc->iv, 8); + + hex_log(s, AV_LOG_DEBUG, "CBC-MAC", &gdata[OMA_ENC_HEADER_SIZE+oc->k_size+oc->e_size+oc->i_size], 8); + + if (s->keylen > 0) { + kset(s, s->key, s->key, s->keylen); + } + if (!memcmp(oc->r_val, (const uint8_t[8]){0}, 8) || + rprobe(s, gdata, oc->r_val) < 0 && + nprobe(s, gdata, geob->datasize, oc->n_val) < 0) { + int i; + for (i = 0; i < FF_ARRAY_ELEMS(leaf_table); i += 2) { + uint8_t buf[16]; + AV_WL64(buf, leaf_table[i]); + AV_WL64(&buf[8], leaf_table[i+1]); + kset(s, buf, buf, 16); + if (!rprobe(s, gdata, oc->r_val) || !nprobe(s, gdata, geob->datasize, oc->n_val)) + break; + } + if (i >= FF_ARRAY_ELEMS(leaf_table)) { + av_log(s, AV_LOG_ERROR, "Invalid key\n"); + return -1; + } + } + + /* e_val */ + av_des_init(&oc->av_des, oc->m_val, 64, 0); + av_des_crypt(&oc->av_des, oc->e_val, &gdata[OMA_ENC_HEADER_SIZE + 40], 1, NULL, 0); + hex_log(s, AV_LOG_DEBUG, "EK", oc->e_val, 8); + + /* init e_val */ + av_des_init(&oc->av_des, oc->e_val, 64, 1); + + return 0; +} + +static int oma_read_header(AVFormatContext *s) +{ + int ret, framesize, jsflag, samplerate; + uint32_t codec_params; + int16_t eid; + uint8_t buf[EA3_HEADER_SIZE]; + uint8_t *edata; + AVStream *st; + ID3v2ExtraMeta *extra_meta = NULL; + OMAContext *oc = s->priv_data; + + ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta); + ret = avio_read(s->pb, buf, EA3_HEADER_SIZE); + if (ret < EA3_HEADER_SIZE) + return -1; + + if (memcmp(buf, ((const uint8_t[]){'E', 'A', '3'}),3) || buf[4] != 0 || buf[5] != EA3_HEADER_SIZE) { + av_log(s, AV_LOG_ERROR, "Couldn't find the EA3 header !\n"); + return -1; + } + + oc->content_start = avio_tell(s->pb); + + /* encrypted file */ + eid = AV_RB16(&buf[6]); + if (eid != -1 && eid != -128 && decrypt_init(s, extra_meta, buf) < 0) { + ff_id3v2_free_extra_meta(&extra_meta); + return -1; + } + + ff_id3v2_free_extra_meta(&extra_meta); + + codec_params = AV_RB24(&buf[33]); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->start_time = 0; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = buf[32]; + st->codec->codec_id = ff_codec_get_id(ff_oma_codec_tags, st->codec->codec_tag); + + switch (buf[32]) { + case OMA_CODECID_ATRAC3: + samplerate = ff_oma_srate_tab[(codec_params >> 13) & 7] * 100; + if (!samplerate) { + av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n"); + return AVERROR_INVALIDDATA; + } + if (samplerate != 44100) + avpriv_request_sample(s, "Sample rate %d", samplerate); + + framesize = (codec_params & 0x3FF) * 8; + jsflag = (codec_params >> 17) & 1; /* get stereo coding mode, 1 for joint-stereo */ + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = samplerate; + st->codec->bit_rate = st->codec->sample_rate * framesize * 8 / 1024; + + /* fake the atrac3 extradata (wav format, makes stream copy to wav work) */ + st->codec->extradata_size = 14; + edata = av_mallocz(14 + FF_INPUT_BUFFER_PADDING_SIZE); + if (!edata) + return AVERROR(ENOMEM); + + st->codec->extradata = edata; + AV_WL16(&edata[0], 1); // always 1 + AV_WL32(&edata[2], samplerate); // samples rate + AV_WL16(&edata[6], jsflag); // coding mode + AV_WL16(&edata[8], jsflag); // coding mode + AV_WL16(&edata[10], 1); // always 1 + // AV_WL16(&edata[12], 0); // always 0 + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + break; + case OMA_CODECID_ATRAC3P: + st->codec->channels = (codec_params >> 10) & 7; + framesize = ((codec_params & 0x3FF) * 8) + 8; + samplerate = ff_oma_srate_tab[(codec_params >> 13) & 7] * 100; + if (!samplerate) { + av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n"); + return AVERROR_INVALIDDATA; + } + st->codec->sample_rate = samplerate; + st->codec->bit_rate = samplerate * framesize * 8 / 1024; + avpriv_set_pts_info(st, 64, 1, samplerate); + av_log(s, AV_LOG_ERROR, "Unsupported codec ATRAC3+!\n"); + break; + case OMA_CODECID_MP3: + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + framesize = 1024; + break; + case OMA_CODECID_LPCM: + /* PCM 44.1 kHz 16 bit stereo big-endian */ + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + st->codec->sample_rate = 44100; + framesize = 1024; + /* bit rate = sample rate x PCM block align (= 4) x 8 */ + st->codec->bit_rate = st->codec->sample_rate * 32; + st->codec->bits_per_coded_sample = av_get_bits_per_sample(st->codec->codec_id); + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + break; + default: + av_log(s, AV_LOG_ERROR, "Unsupported codec %d!\n",buf[32]); + return -1; + } + + st->codec->block_align = framesize; + + return 0; +} + + +static int oma_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + OMAContext *oc = s->priv_data; + int packet_size = s->streams[0]->codec->block_align; + int ret = av_get_packet(s->pb, pkt, packet_size); + + if (ret <= 0) + return AVERROR(EIO); + + pkt->stream_index = 0; + + if (oc->encrypted) { + /* previous unencrypted block saved in IV for the next packet (CBC mode) */ + av_des_crypt(&oc->av_des, pkt->data, pkt->data, (ret >> 3), oc->iv, 1); + } + + return ret; +} + +static int oma_read_probe(AVProbeData *p) +{ + const uint8_t *buf; + unsigned tag_len = 0; + + buf = p->buf; + + if (p->buf_size < ID3v2_HEADER_SIZE || + !ff_id3v2_match(buf, ID3v2_EA3_MAGIC) || + buf[3] != 3 || // version must be 3 + buf[4]) // flags byte zero + return 0; + + tag_len = ff_id3v2_tag_len(buf); + + /* This check cannot overflow as tag_len has at most 28 bits */ + if (p->buf_size < tag_len + 5) + /* EA3 header comes late, might be outside of the probe buffer */ + return AVPROBE_SCORE_MAX / 2; + + buf += tag_len; + + if (!memcmp(buf, "EA3", 3) && !buf[4] && buf[5] == EA3_HEADER_SIZE) + return AVPROBE_SCORE_MAX; + else + return 0; +} + +static int oma_read_seek(struct AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + OMAContext *oc = s->priv_data; + + ff_pcm_read_seek(s, stream_index, timestamp, flags); + + if (oc->encrypted) { + /* readjust IV for CBC */ + int64_t pos = avio_tell(s->pb); + if (pos < oc->content_start) + memset(oc->iv, 0, 8); + else { + if (avio_seek(s->pb, -8, SEEK_CUR) < 0 || avio_read(s->pb, oc->iv, 8) < 8) { + memset(oc->iv, 0, 8); + return -1; + } + } + } + + return 0; +} + +AVInputFormat ff_oma_demuxer = { + .name = "oma", + .long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), + .priv_data_size = sizeof(OMAContext), + .read_probe = oma_read_probe, + .read_header = oma_read_header, + .read_packet = oma_read_packet, + .read_seek = oma_read_seek, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "oma,omg,aa3", + .codec_tag = (const AVCodecTag* const []){ff_oma_codec_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omadec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/omadec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/omadec.o libavformat/omadec.o: libavformat/omadec.c \ + libavutil/channel_layout.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/attributes.h libavutil/version.h libavutil/avconfig.h \ + config.h libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h config.h \ + libavutil/attributes.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavutil/intreadwrite.h libavutil/bswap.h libavutil/x86/bswap.h \ + libavutil/x86/intreadwrite.h libavutil/des.h libavformat/oma.h \ + libavformat/pcm.h libavformat/id3v2.h libavformat/metadata.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omadec.o Binary file ffmpeg/libavformat/omadec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omaenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/omaenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,106 @@ +/* + * Sony OpenMG (OMA) muxer + * + * Copyright (c) 2011 Michael Karcher + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "avio_internal.h" +#include "id3v2.h" +#include "internal.h" +#include "oma.h" +#include "rawenc.h" + +static av_cold int oma_write_header(AVFormatContext *s) +{ + int i; + AVCodecContext *format; + int srate_index; + int isjointstereo; + + format = s->streams[0]->codec; + /* check for support of the format first */ + + for (srate_index = 0; ; srate_index++) { + if (ff_oma_srate_tab[srate_index] == 0) { + av_log(s, AV_LOG_ERROR, "Sample rate %d not supported in OpenMG audio\n", + format->sample_rate); + return AVERROR(EINVAL); + } + + if (ff_oma_srate_tab[srate_index] * 100 == format->sample_rate) + break; + } + + /* Metadata; OpenMG does not support ID3v2.4 */ + ff_id3v2_write_simple(s, 3, ID3v2_EA3_MAGIC); + + ffio_wfourcc(s->pb, "EA3\0"); + avio_w8(s->pb, EA3_HEADER_SIZE >> 7); + avio_w8(s->pb, EA3_HEADER_SIZE & 0x7F); + avio_wl16(s->pb, 0xFFFF); /* not encrypted */ + for (i = 0; i < 6; i++) + avio_wl32(s->pb, 0); /* Padding + DRM id */ + + switch(format->codec_tag) { + case OMA_CODECID_ATRAC3: + if (format->channels != 2) { + av_log(s, AV_LOG_ERROR, "ATRAC3 in OMA is only supported with 2 channels\n"); + return AVERROR(EINVAL); + } + if (format->extradata_size == 14) /* WAV format extradata */ + isjointstereo = format->extradata[6] != 0; + else if(format->extradata_size == 10) /* RM format extradata */ + isjointstereo = format->extradata[8] == 0x12; + else { + av_log(s, AV_LOG_ERROR, "ATRAC3: Unsupported extradata size\n"); + return AVERROR(EINVAL); + } + avio_wb32(s->pb, (OMA_CODECID_ATRAC3 << 24) | + (isjointstereo << 17) | + (srate_index << 13) | + (format->block_align/8)); + break; + case OMA_CODECID_ATRAC3P: + avio_wb32(s->pb, (OMA_CODECID_ATRAC3P << 24) | + (srate_index << 13) | + (format->channels << 10) | + (format->block_align/8 - 1)); + break; + default: + av_log(s, AV_LOG_ERROR, "unsupported codec tag %d for write\n", + format->codec_tag); + return AVERROR(EINVAL); + } + for (i = 0; i < (EA3_HEADER_SIZE - 36)/4; i++) + avio_wl32(s->pb, 0); /* Padding */ + + return 0; +} + +AVOutputFormat ff_oma_muxer = { + .name = "oma", + .long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), + .mime_type = "audio/x-oma", + .extensions = "oma", + .audio_codec = AV_CODEC_ID_ATRAC3, + .write_header = oma_write_header, + .write_packet = ff_raw_write_packet, + .codec_tag = (const AVCodecTag* const []){ff_oma_codec_tags, 0}, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omaenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/omaenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/omaenc.o libavformat/omaenc.o: libavformat/omaenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavformat/id3v2.h libavformat/internal.h \ + libavformat/metadata.h libavformat/oma.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/omaenc.o Binary file ffmpeg/libavformat/omaenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/options.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/options.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "avformat.h" +#include "avio_internal.h" +#include "libavutil/opt.h" + +/** + * @file + * Options definition for AVFormatContext. + */ + +#include "options_table.h" + +static const char* format_to_name(void* ptr) +{ + AVFormatContext* fc = (AVFormatContext*) ptr; + if(fc->iformat) return fc->iformat->name; + else if(fc->oformat) return fc->oformat->name; + else return "NULL"; +} + +static void *format_child_next(void *obj, void *prev) +{ + AVFormatContext *s = obj; + if (!prev && s->priv_data && + ((s->iformat && s->iformat->priv_class) || + s->oformat && s->oformat->priv_class)) + return s->priv_data; + if (s->pb && s->pb->av_class && prev != s->pb) + return s->pb; + return NULL; +} + +static const AVClass *format_child_class_next(const AVClass *prev) +{ + AVInputFormat *ifmt = NULL; + AVOutputFormat *ofmt = NULL; + + if (!prev) + return &ffio_url_class; + + while ((ifmt = av_iformat_next(ifmt))) + if (ifmt->priv_class == prev) + break; + + if (!ifmt) + while ((ofmt = av_oformat_next(ofmt))) + if (ofmt->priv_class == prev) + break; + if (!ofmt) + while (ifmt = av_iformat_next(ifmt)) + if (ifmt->priv_class) + return ifmt->priv_class; + + while (ofmt = av_oformat_next(ofmt)) + if (ofmt->priv_class) + return ofmt->priv_class; + + return NULL; +} + +static AVClassCategory get_category(void *ptr) +{ + AVFormatContext* s = ptr; + if(s->iformat) return AV_CLASS_CATEGORY_DEMUXER; + else return AV_CLASS_CATEGORY_MUXER; +} + +static const AVClass av_format_context_class = { + .class_name = "AVFormatContext", + .item_name = format_to_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .child_next = format_child_next, + .child_class_next = format_child_class_next, + .category = AV_CLASS_CATEGORY_MUXER, + .get_category = get_category, +}; + +static void avformat_get_context_defaults(AVFormatContext *s) +{ + memset(s, 0, sizeof(AVFormatContext)); + + s->av_class = &av_format_context_class; + + av_opt_set_defaults(s); +} + +AVFormatContext *avformat_alloc_context(void) +{ + AVFormatContext *ic; + ic = av_malloc(sizeof(AVFormatContext)); + if (!ic) return ic; + avformat_get_context_defaults(ic); + return ic; +} + +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx) +{ + return ctx->duration_estimation_method; +} + +const AVClass *avformat_get_class(void) +{ + return &av_format_context_class; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/options.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/options.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/options.o libavformat/options.o: libavformat/options.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/avio_internal.h \ + libavformat/url.h libavutil/opt.h libavformat/options_table.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/options.o Binary file ffmpeg/libavformat/options.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/options_table.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/options_table.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_OPTIONS_TABLE_H +#define AVFORMAT_OPTIONS_TABLE_H + +#include + +#include "libavutil/opt.h" +#include "avformat.h" + +#define OFFSET(x) offsetof(AVFormatContext,x) +#define DEFAULT 0 //should be NAN but it does not work as it is not a constant in glibc as required by ANSI/ISO C +//these names are too long to be readable +#define E AV_OPT_FLAG_ENCODING_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[]={ +{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"}, +{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"}, +{"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT, {.i64 = 5000000 }, 32, INT_MAX, D}, +{"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E}, +{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "fflags"}, +{"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"}, +{"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, "fflags"}, +{"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, "fflags"}, +{"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"}, +{"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"}, +{"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"}, +{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"}, +{"keepside", "dont merge side data", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"}, +{"latm", "enable RTP MP4A-LATM payload", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"}, +{"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, +{"seek2any", "forces seeking to enable seek to any mode", OFFSET(seek2any), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, D}, +{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT, {.i64 = 5*AV_TIME_BASE }, 0, INT_MAX, D}, +{"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, +{"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D}, +{"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), AV_OPT_TYPE_INT, {.i64 = 3041280 }, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */ +{"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, "fdebug"}, +{"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"}, +{"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D}, +{"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D}, +{"audio_preload", "microseconds by which audio packets should be interleaved earlier", OFFSET(audio_preload), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_duration", "microseconds for each chunk", OFFSET(max_chunk_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +{"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, +/* this is a crutch for avconv, since it cannot deal with identically named options in different contexts. + * to be removed when avconv is fixed */ +{"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, +{"err_detect", "set error detection flags", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, +{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, +{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"}, +{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"}, +{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"}, +{"careful", "consider things that violate the spec and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"}, +{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, D, "err_detect"}, +{"aggressive", "consider things that a sane encoder shouldnt do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, D, "err_detect"}, +{"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D}, +{"avoid_negative_ts", "shift timestamps to make them positive. 1 enables, 0 disables, default of -1 enables when required by target format.", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, E}, +{"skip_initial_bytes", "skip initial bytes", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, D}, +{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, +{"flush_packets", "enable flushing of the I/O context after each packet", OFFSET(flush_packets), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E}, +{NULL}, +}; + +#undef E +#undef D +#undef DEFAULT +#undef OFFSET + +#endif /* AVFORMAT_OPTIONS_TABLE_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/os_support.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/os_support.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,380 @@ +/* + * various OS-feature replacement utilities + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * copyright (c) 2002 Francois Revol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* needed by inet_aton() */ +#define _SVID_SOURCE + +#include "config.h" +#include "avformat.h" +#include "os_support.h" + +#if defined(_WIN32) && !defined(__MINGW32CE__) +#undef open +#undef lseek +#undef stat +#undef fstat +#include +#include +#include +#include +#include + +int ff_win32_open(const char *filename_utf8, int oflag, int pmode) +{ + int fd; + int num_chars; + wchar_t *filename_w; + + /* convert UTF-8 to wide chars */ + num_chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename_utf8, -1, NULL, 0); + if (num_chars <= 0) + goto fallback; + filename_w = av_mallocz(sizeof(wchar_t) * num_chars); + if (!filename_w) { + errno = ENOMEM; + return -1; + } + MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, filename_w, num_chars); + + fd = _wsopen(filename_w, oflag, SH_DENYNO, pmode); + av_freep(&filename_w); + + if (fd != -1 || (oflag & O_CREAT)) + return fd; + +fallback: + /* filename may be be in CP_ACP */ + return _sopen(filename_utf8, oflag, SH_DENYNO, pmode); +} +#endif + +#if CONFIG_NETWORK +#include +#if !HAVE_POLL_H +#if HAVE_SYS_TIME_H +#include +#endif +#if HAVE_WINSOCK2_H +#include +#elif HAVE_SYS_SELECT_H +#include +#endif +#endif + +#include "network.h" + +#if !HAVE_INET_ATON +#include + +int ff_inet_aton(const char *str, struct in_addr *add) +{ + unsigned int add1 = 0, add2 = 0, add3 = 0, add4 = 0; + + if (sscanf(str, "%d.%d.%d.%d", &add1, &add2, &add3, &add4) != 4) + return 0; + + if (!add1 || (add1 | add2 | add3 | add4) > 255) + return 0; + + add->s_addr = htonl((add1 << 24) + (add2 << 16) + (add3 << 8) + add4); + + return 1; +} +#else +int ff_inet_aton(const char *str, struct in_addr *add) +{ + return inet_aton(str, add); +} +#endif /* !HAVE_INET_ATON */ + +#if !HAVE_GETADDRINFO +int ff_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *h = NULL; + struct addrinfo *ai; + struct sockaddr_in *sin; + +#if HAVE_WINSOCK2_H + int (WSAAPI *win_getaddrinfo)(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + HMODULE ws2mod = GetModuleHandle("ws2_32.dll"); + win_getaddrinfo = GetProcAddress(ws2mod, "getaddrinfo"); + if (win_getaddrinfo) + return win_getaddrinfo(node, service, hints, res); +#endif + + *res = NULL; + sin = av_mallocz(sizeof(struct sockaddr_in)); + if (!sin) + return EAI_FAIL; + sin->sin_family = AF_INET; + + if (node) { + if (!ff_inet_aton(node, &sin->sin_addr)) { + if (hints && (hints->ai_flags & AI_NUMERICHOST)) { + av_free(sin); + return EAI_FAIL; + } + h = gethostbyname(node); + if (!h) { + av_free(sin); + return EAI_FAIL; + } + memcpy(&sin->sin_addr, h->h_addr_list[0], sizeof(struct in_addr)); + } + } else { + if (hints && (hints->ai_flags & AI_PASSIVE)) + sin->sin_addr.s_addr = INADDR_ANY; + else + sin->sin_addr.s_addr = INADDR_LOOPBACK; + } + + /* Note: getaddrinfo allows service to be a string, which + * should be looked up using getservbyname. */ + if (service) + sin->sin_port = htons(atoi(service)); + + ai = av_mallocz(sizeof(struct addrinfo)); + if (!ai) { + av_free(sin); + return EAI_FAIL; + } + + *res = ai; + ai->ai_family = AF_INET; + ai->ai_socktype = hints ? hints->ai_socktype : 0; + switch (ai->ai_socktype) { + case SOCK_STREAM: + ai->ai_protocol = IPPROTO_TCP; + break; + case SOCK_DGRAM: + ai->ai_protocol = IPPROTO_UDP; + break; + default: + ai->ai_protocol = 0; + break; + } + + ai->ai_addr = (struct sockaddr *)sin; + ai->ai_addrlen = sizeof(struct sockaddr_in); + if (hints && (hints->ai_flags & AI_CANONNAME)) + ai->ai_canonname = h ? av_strdup(h->h_name) : NULL; + + ai->ai_next = NULL; + return 0; +} + +void ff_freeaddrinfo(struct addrinfo *res) +{ +#if HAVE_WINSOCK2_H + void (WSAAPI *win_freeaddrinfo)(struct addrinfo *res); + HMODULE ws2mod = GetModuleHandle("ws2_32.dll"); + win_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *res)) + GetProcAddress(ws2mod, "freeaddrinfo"); + if (win_freeaddrinfo) { + win_freeaddrinfo(res); + return; + } +#endif + + av_free(res->ai_canonname); + av_free(res->ai_addr); + av_free(res); +} + +int ff_getnameinfo(const struct sockaddr *sa, int salen, + char *host, int hostlen, + char *serv, int servlen, int flags) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; + +#if HAVE_WINSOCK2_H + int (WSAAPI *win_getnameinfo)(const struct sockaddr *sa, socklen_t salen, + char *host, DWORD hostlen, + char *serv, DWORD servlen, int flags); + HMODULE ws2mod = GetModuleHandle("ws2_32.dll"); + win_getnameinfo = GetProcAddress(ws2mod, "getnameinfo"); + if (win_getnameinfo) + return win_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); +#endif + + if (sa->sa_family != AF_INET) + return EAI_FAMILY; + if (!host && !serv) + return EAI_NONAME; + + if (host && hostlen > 0) { + struct hostent *ent = NULL; + uint32_t a; + if (!(flags & NI_NUMERICHOST)) + ent = gethostbyaddr((const char *)&sin->sin_addr, + sizeof(sin->sin_addr), AF_INET); + + if (ent) { + snprintf(host, hostlen, "%s", ent->h_name); + } else if (flags & NI_NAMERQD) { + return EAI_NONAME; + } else { + a = ntohl(sin->sin_addr.s_addr); + snprintf(host, hostlen, "%d.%d.%d.%d", + ((a >> 24) & 0xff), ((a >> 16) & 0xff), + ((a >> 8) & 0xff), (a & 0xff)); + } + } + + if (serv && servlen > 0) { + struct servent *ent = NULL; +#if HAVE_GETSERVBYPORT + if (!(flags & NI_NUMERICSERV)) + ent = getservbyport(sin->sin_port, flags & NI_DGRAM ? "udp" : "tcp"); +#endif + + if (ent) + snprintf(serv, servlen, "%s", ent->s_name); + else + snprintf(serv, servlen, "%d", ntohs(sin->sin_port)); + } + + return 0; +} +#endif /* !HAVE_GETADDRINFO */ + +#if !HAVE_GETADDRINFO || HAVE_WINSOCK2_H +const char *ff_gai_strerror(int ecode) +{ + switch (ecode) { + case EAI_AGAIN: + return "Temporary failure in name resolution"; + case EAI_BADFLAGS: + return "Invalid flags for ai_flags"; + case EAI_FAIL: + return "A non-recoverable error occurred"; + case EAI_FAMILY: + return "The address family was not recognized or the address " + "length was invalid for the specified family"; + case EAI_MEMORY: + return "Memory allocation failure"; +#if EAI_NODATA != EAI_NONAME + case EAI_NODATA: + return "No address associated with hostname"; +#endif + case EAI_NONAME: + return "The name does not resolve for the supplied parameters"; + case EAI_SERVICE: + return "servname not supported for ai_socktype"; + case EAI_SOCKTYPE: + return "ai_socktype not supported"; + } + + return "Unknown error"; +} +#endif /* !HAVE_GETADDRINFO || HAVE_WINSOCK2_H */ + +int ff_socket_nonblock(int socket, int enable) +{ +#if HAVE_WINSOCK2_H + u_long param = enable; + return ioctlsocket(socket, FIONBIO, ¶m); +#else + if (enable) + return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK); + else + return fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK); +#endif +} + +#if !HAVE_POLL_H +int ff_poll(struct pollfd *fds, nfds_t numfds, int timeout) +{ + fd_set read_set; + fd_set write_set; + fd_set exception_set; + nfds_t i; + int n; + int rc; + +#if HAVE_WINSOCK2_H + if (numfds >= FD_SETSIZE) { + errno = EINVAL; + return -1; + } +#endif + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&exception_set); + + n = 0; + for (i = 0; i < numfds; i++) { + if (fds[i].fd < 0) + continue; +#if !HAVE_WINSOCK2_H + if (fds[i].fd >= FD_SETSIZE) { + errno = EINVAL; + return -1; + } +#endif + + if (fds[i].events & POLLIN) + FD_SET(fds[i].fd, &read_set); + if (fds[i].events & POLLOUT) + FD_SET(fds[i].fd, &write_set); + if (fds[i].events & POLLERR) + FD_SET(fds[i].fd, &exception_set); + + if (fds[i].fd >= n) + n = fds[i].fd + 1; + } + + if (n == 0) + /* Hey!? Nothing to poll, in fact!!! */ + return 0; + + if (timeout < 0) { + rc = select(n, &read_set, &write_set, &exception_set, NULL); + } else { + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = 1000 * (timeout % 1000); + rc = select(n, &read_set, &write_set, &exception_set, &tv); + } + + if (rc < 0) + return rc; + + for (i = 0; i < numfds; i++) { + fds[i].revents = 0; + + if (FD_ISSET(fds[i].fd, &read_set)) + fds[i].revents |= POLLIN; + if (FD_ISSET(fds[i].fd, &write_set)) + fds[i].revents |= POLLOUT; + if (FD_ISSET(fds[i].fd, &exception_set)) + fds[i].revents |= POLLERR; + } + + return rc; +} +#endif /* HAVE_POLL_H */ +#endif /* CONFIG_NETWORK */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/os_support.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/os_support.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/os_support.o libavformat/os_support.o: \ + libavformat/os_support.c config.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/os_support.h \ + libavformat/network.h libavutil/error.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/os_support.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/os_support.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,138 @@ +/* + * various OS-feature replacement utilities + * copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_OS_SUPPORT_H +#define AVFORMAT_OS_SUPPORT_H + +/** + * @file + * miscellaneous OS support macros and functions. + */ + +#include "config.h" + +#include + +#if defined(_WIN32) && !defined(__MINGW32CE__) +# include +# ifdef lseek +# undef lseek +# endif +# define lseek(f,p,w) _lseeki64((f), (p), (w)) +# ifdef stat +# undef stat +# endif +# define stat _stati64 +# ifdef fstat +# undef fstat +# endif +# define fstat(f,s) _fstati64((f), (s)) +#endif /* defined(__MINGW32__) && !defined(__MINGW32CE__) */ + +#ifdef _WIN32 +#if HAVE_DIRECT_H +#include +#elif HAVE_IO_H +#include +#endif +#define mkdir(a, b) _mkdir(a) +#else +#include +#endif + +static inline int is_dos_path(const char *path) +{ +#if HAVE_DOS_PATHS + if (path[0] && path[1] == ':') + return 1; +#endif + return 0; +} + +#if defined(__OS2__) || defined(__Plan9__) +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 +#endif + +#if defined(_WIN32) +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH + +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif +#endif + +#if defined(_WIN32) && !defined(__MINGW32CE__) +int ff_win32_open(const char *filename, int oflag, int pmode); +#define open ff_win32_open +#endif + +#if CONFIG_NETWORK +#if !HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +/* most of the time closing a socket is just closing an fd */ +#if !HAVE_CLOSESOCKET +#define closesocket close +#endif + +#if !HAVE_POLL_H +typedef unsigned long nfds_t; + +#if HAVE_WINSOCK2_H +#include +#endif +#if !HAVE_STRUCT_POLLFD +struct pollfd { + int fd; + short events; /* events to look for */ + short revents; /* events that occurred */ +}; + +/* events & revents */ +#define POLLIN 0x0001 /* any readable data available */ +#define POLLOUT 0x0002 /* file descriptor is writeable */ +#define POLLRDNORM POLLIN +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0008 /* priority readable data */ +#define POLLWRBAND 0x0010 /* priority data can be written */ +#define POLLPRI 0x0020 /* high priority readable data */ + +/* revents only */ +#define POLLERR 0x0004 /* errors pending */ +#define POLLHUP 0x0080 /* disconnected */ +#define POLLNVAL 0x1000 /* invalid file descriptor */ +#endif + + +int ff_poll(struct pollfd *fds, nfds_t numfds, int timeout); +#define poll ff_poll +#endif /* HAVE_POLL_H */ +#endif /* CONFIG_NETWORK */ + +#endif /* AVFORMAT_OS_SUPPORT_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/os_support.o Binary file ffmpeg/libavformat/os_support.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/paf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/paf.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,261 @@ +/* + * Packed Animation File demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/channel_layout.h" +#include "libavcodec/paf.h" +#include "avformat.h" +#include "internal.h" + +#define MAGIC "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a" + +typedef struct { + uint32_t buffer_size; + uint32_t frame_blks; + uint32_t nb_frames; + uint32_t start_offset; + uint32_t preload_count; + uint32_t max_video_blks; + uint32_t max_audio_blks; + + uint32_t current_frame; + uint32_t current_frame_count; + uint32_t current_frame_block; + + uint32_t *blocks_count_table; + uint32_t *frames_offset_table; + uint32_t *blocks_offset_table; + + uint8_t *video_frame; + int video_size; + + uint8_t *audio_frame; + uint8_t *temp_audio_frame; + int audio_size; + + int got_audio; +} PAFDemuxContext; + +static int read_probe(AVProbeData *p) +{ + if ((p->buf_size >= strlen(MAGIC)) && + !memcmp(p->buf, MAGIC, strlen(MAGIC))) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int read_close(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + + av_freep(&p->blocks_count_table); + av_freep(&p->frames_offset_table); + av_freep(&p->blocks_offset_table); + av_freep(&p->video_frame); + av_freep(&p->audio_frame); + av_freep(&p->temp_audio_frame); + + return 0; +} + +static void read_table(AVFormatContext *s, uint32_t *table, uint32_t count) +{ + int i; + + for (i = 0; i < count; i++) + table[i] = avio_rl32(s->pb); + + avio_skip(s->pb, 4 * (FFALIGN(count, 512) - count)); +} + +static int read_header(AVFormatContext *s) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *ast, *vst; + int ret = 0; + + avio_skip(pb, 132); + + vst = avformat_new_stream(s, 0); + if (!vst) + return AVERROR(ENOMEM); + + vst->start_time = 0; + vst->nb_frames = + vst->duration = + p->nb_frames = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->width = avio_rl32(pb); + vst->codec->height = avio_rl32(pb); + avio_skip(pb, 4); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codec->codec_tag = 0; + vst->codec->codec_id = AV_CODEC_ID_PAF_VIDEO; + avpriv_set_pts_info(vst, 64, 1, 10); + + ast = avformat_new_stream(s, 0); + if (!ast) + return AVERROR(ENOMEM); + + ast->start_time = 0; + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_tag = 0; + ast->codec->codec_id = AV_CODEC_ID_PAF_AUDIO; + ast->codec->channels = 2; + ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; + ast->codec->sample_rate = 22050; + avpriv_set_pts_info(ast, 64, 1, 22050); + + p->buffer_size = avio_rl32(pb); + p->preload_count = avio_rl32(pb); + p->frame_blks = avio_rl32(pb); + p->start_offset = avio_rl32(pb); + p->max_video_blks = avio_rl32(pb); + p->max_audio_blks = avio_rl32(pb); + if (p->buffer_size < 175 || + p->max_audio_blks < 2 || + p->max_video_blks < 1 || + p->frame_blks < 1 || + p->nb_frames < 1 || + p->preload_count < 1 || + p->buffer_size > 2048 || + p->max_video_blks > 2048 || + p->max_audio_blks > 2048 || + p->nb_frames > INT_MAX / sizeof(uint32_t) || + p->frame_blks > INT_MAX / sizeof(uint32_t)) + return AVERROR_INVALIDDATA; + + p->blocks_count_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->frames_offset_table = av_mallocz(p->nb_frames * sizeof(uint32_t)); + p->blocks_offset_table = av_mallocz(p->frame_blks * sizeof(uint32_t)); + + p->video_size = p->max_video_blks * p->buffer_size; + p->video_frame = av_mallocz(p->video_size); + + p->audio_size = p->max_audio_blks * p->buffer_size; + p->audio_frame = av_mallocz(p->audio_size); + p->temp_audio_frame = av_mallocz(p->audio_size); + + if (!p->blocks_count_table || + !p->frames_offset_table || + !p->blocks_offset_table || + !p->video_frame || + !p->audio_frame || + !p->temp_audio_frame) { + ret = AVERROR(ENOMEM); + goto fail; + } + + avio_seek(pb, p->buffer_size, SEEK_SET); + + read_table(s, p->blocks_count_table, p->nb_frames); + read_table(s, p->frames_offset_table, p->nb_frames); + read_table(s, p->blocks_offset_table, p->frame_blks); + + p->got_audio = 0; + p->current_frame = 0; + p->current_frame_block = 0; + + avio_seek(pb, p->start_offset, SEEK_SET); + + return 0; + +fail: + read_close(s); + + return ret; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PAFDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + uint32_t count, offset; + int size, i; + + if (p->current_frame >= p->nb_frames) + return AVERROR_EOF; + + if (url_feof(pb)) + return AVERROR_EOF; + + if (p->got_audio) { + if (av_new_packet(pkt, p->audio_size) < 0) + return AVERROR(ENOMEM); + + memcpy(pkt->data, p->temp_audio_frame, p->audio_size); + pkt->duration = PAF_SOUND_SAMPLES * (p->audio_size / PAF_SOUND_FRAME_SIZE); + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = 1; + p->got_audio = 0; + return pkt->size; + } + + count = (p->current_frame == 0) ? p->preload_count : p->blocks_count_table[p->current_frame - 1]; + for (i = 0; i < count; i++) { + if (p->current_frame_block >= p->frame_blks) + return AVERROR_INVALIDDATA; + + offset = p->blocks_offset_table[p->current_frame_block] & ~(1U << 31); + if (p->blocks_offset_table[p->current_frame_block] & (1U << 31)) { + if (offset > p->audio_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->audio_frame + offset, p->buffer_size); + if (offset == (p->max_audio_blks - 2) * p->buffer_size) { + memcpy(p->temp_audio_frame, p->audio_frame, p->audio_size); + p->got_audio = 1; + } + } else { + if (offset > p->video_size - p->buffer_size) + return AVERROR_INVALIDDATA; + + avio_read(pb, p->video_frame + offset, p->buffer_size); + } + p->current_frame_block++; + } + + size = p->video_size - p->frames_offset_table[p->current_frame]; + if (size < 1) + return AVERROR_INVALIDDATA; + + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); + + pkt->stream_index = 0; + pkt->duration = 1; + memcpy(pkt->data, p->video_frame + p->frames_offset_table[p->current_frame], size); + if (pkt->data[0] & 0x20) + pkt->flags |= AV_PKT_FLAG_KEY; + p->current_frame++; + + return pkt->size; +} + +AVInputFormat ff_paf_demuxer = { + .name = "paf", + .long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File"), + .priv_data_size = sizeof(PAFDemuxContext), + .read_probe = read_probe, + .read_header = read_header, + .read_packet = read_packet, + .read_close = read_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/paf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/paf.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/paf.o libavformat/paf.o: libavformat/paf.c \ + libavutil/channel_layout.h libavcodec/paf.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/attributes.h libavutil/version.h \ + libavutil/avconfig.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/paf.o Binary file ffmpeg/libavformat/paf.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcm.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,76 @@ +/* + * PCM common functions + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "pcm.h" + +#define RAW_SAMPLES 1024 + +int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size; + + size= RAW_SAMPLES*s->streams[0]->codec->block_align; + if (size <= 0) + return AVERROR(EINVAL); + + ret= av_get_packet(s->pb, pkt, size); + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + if (ret < 0) + return ret; + + return ret; +} + +int ff_pcm_read_seek(AVFormatContext *s, + int stream_index, int64_t timestamp, int flags) +{ + AVStream *st; + int block_align, byte_rate; + int64_t pos, ret; + + st = s->streams[0]; + + block_align = st->codec->block_align ? st->codec->block_align : + (av_get_bits_per_sample(st->codec->codec_id) * st->codec->channels) >> 3; + byte_rate = st->codec->bit_rate ? st->codec->bit_rate >> 3 : + block_align * st->codec->sample_rate; + + if (block_align <= 0 || byte_rate <= 0) + return -1; + if (timestamp < 0) timestamp = 0; + + /* compute the position by aligning it to block_align */ + pos = av_rescale_rnd(timestamp * byte_rate, + st->time_base.num, + st->time_base.den * (int64_t)block_align, + (flags & AVSEEK_FLAG_BACKWARD) ? AV_ROUND_DOWN : AV_ROUND_UP); + pos *= block_align; + + /* recompute exact position */ + st->cur_dts = av_rescale(pos, st->time_base.den, byte_rate * (int64_t)st->time_base.num); + if ((ret = avio_seek(s->pb, pos + s->data_offset, SEEK_SET)) < 0) + return ret; + return 0; +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcm.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/pcm.o libavformat/pcm.o: libavformat/pcm.c \ + libavutil/mathematics.h libavutil/attributes.h libavutil/rational.h \ + libavutil/intfloat.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcm.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,31 @@ +/* + * PCM common functions + * Copyright (C) 2007 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_PCM_H +#define AVFORMAT_PCM_H + +#include "avformat.h" + +int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt); +int ff_pcm_read_seek(AVFormatContext *s, + int stream_index, int64_t timestamp, int flags); + +#endif /* AVFORMAT_PCM_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcm.o Binary file ffmpeg/libavformat/pcm.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcmdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,146 @@ +/* + * RAW PCM demuxers + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "pcm.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" + +typedef struct PCMAudioDemuxerContext { + AVClass *class; + int sample_rate; + int channels; +} PCMAudioDemuxerContext; + +static int pcm_read_header(AVFormatContext *s) +{ + PCMAudioDemuxerContext *s1 = s->priv_data; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->codec->sample_rate = s1->sample_rate; + st->codec->channels = s1->channels; + + st->codec->bits_per_coded_sample = + av_get_bits_per_sample(st->codec->codec_id); + + av_assert0(st->codec->bits_per_coded_sample > 0); + + st->codec->block_align = + st->codec->bits_per_coded_sample * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + return 0; +} + +static const AVOption pcm_options[] = { + { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +#define PCMDEF(name_, long_name_, ext, codec) \ +static const AVClass name_ ## _demuxer_class = { \ + .class_name = #name_ " demuxer", \ + .item_name = av_default_item_name, \ + .option = pcm_options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}; \ +AVInputFormat ff_pcm_ ## name_ ## _demuxer = { \ + .name = #name_, \ + .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ + .priv_data_size = sizeof(PCMAudioDemuxerContext), \ + .read_header = pcm_read_header, \ + .read_packet = ff_pcm_read_packet, \ + .read_seek = ff_pcm_read_seek, \ + .flags = AVFMT_GENERIC_INDEX, \ + .extensions = ext, \ + .raw_codec_id = codec, \ + .priv_class = &name_ ## _demuxer_class, \ +}; + +PCMDEF(f64be, "PCM 64-bit floating-point big-endian", + NULL, AV_CODEC_ID_PCM_F64BE) + +PCMDEF(f64le, "PCM 64-bit floating-point little-endian", + NULL, AV_CODEC_ID_PCM_F64LE) + +PCMDEF(f32be, "PCM 32-bit floating-point big-endian", + NULL, AV_CODEC_ID_PCM_F32BE) + +PCMDEF(f32le, "PCM 32-bit floating-point little-endian", + NULL, AV_CODEC_ID_PCM_F32LE) + +PCMDEF(s32be, "PCM signed 32-bit big-endian", + NULL, AV_CODEC_ID_PCM_S32BE) + +PCMDEF(s32le, "PCM signed 32-bit little-endian", + NULL, AV_CODEC_ID_PCM_S32LE) + +PCMDEF(s24be, "PCM signed 24-bit big-endian", + NULL, AV_CODEC_ID_PCM_S24BE) + +PCMDEF(s24le, "PCM signed 24-bit little-endian", + NULL, AV_CODEC_ID_PCM_S24LE) + +PCMDEF(s16be, "PCM signed 16-bit big-endian", + AV_NE("sw", NULL), AV_CODEC_ID_PCM_S16BE) + +PCMDEF(s16le, "PCM signed 16-bit little-endian", + AV_NE(NULL, "sw"), AV_CODEC_ID_PCM_S16LE) + +PCMDEF(s8, "PCM signed 8-bit", + "sb", AV_CODEC_ID_PCM_S8) + +PCMDEF(u32be, "PCM unsigned 32-bit big-endian", + NULL, AV_CODEC_ID_PCM_U32BE) + +PCMDEF(u32le, "PCM unsigned 32-bit little-endian", + NULL, AV_CODEC_ID_PCM_U32LE) + +PCMDEF(u24be, "PCM unsigned 24-bit big-endian", + NULL, AV_CODEC_ID_PCM_U24BE) + +PCMDEF(u24le, "PCM unsigned 24-bit little-endian", + NULL, AV_CODEC_ID_PCM_U24LE) + +PCMDEF(u16be, "PCM unsigned 16-bit big-endian", + AV_NE("uw", NULL), AV_CODEC_ID_PCM_U16BE) + +PCMDEF(u16le, "PCM unsigned 16-bit little-endian", + AV_NE(NULL, "uw"), AV_CODEC_ID_PCM_U16LE) + +PCMDEF(u8, "PCM unsigned 8-bit", + "ub", AV_CODEC_ID_PCM_U8) + +PCMDEF(alaw, "PCM A-law", + "al", AV_CODEC_ID_PCM_ALAW) + +PCMDEF(mulaw, "PCM mu-law", + "ul", AV_CODEC_ID_PCM_MULAW) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcmdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/pcmdec.o libavformat/pcmdec.o: libavformat/pcmdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h libavutil/opt.h libavutil/avassert.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmdec.o Binary file ffmpeg/libavformat/pcmdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcmenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,94 @@ +/* + * RAW PCM muxers + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + +#define PCMDEF(name_, long_name_, ext, codec) \ +AVOutputFormat ff_pcm_ ## name_ ## _muxer = { \ + .name = #name_, \ + .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ + .extensions = ext, \ + .audio_codec = codec, \ + .video_codec = AV_CODEC_ID_NONE, \ + .write_packet = ff_raw_write_packet, \ + .flags = AVFMT_NOTIMESTAMPS, \ +}; + +PCMDEF(f64be, "PCM 64-bit floating-point big-endian", + NULL, AV_CODEC_ID_PCM_F64BE) + +PCMDEF(f64le, "PCM 64-bit floating-point little-endian", + NULL, AV_CODEC_ID_PCM_F64LE) + +PCMDEF(f32be, "PCM 32-bit floating-point big-endian", + NULL, AV_CODEC_ID_PCM_F32BE) + +PCMDEF(f32le, "PCM 32-bit floating-point little-endian", + NULL, AV_CODEC_ID_PCM_F32LE) + +PCMDEF(s32be, "PCM signed 32-bit big-endian", + NULL, AV_CODEC_ID_PCM_S32BE) + +PCMDEF(s32le, "PCM signed 32-bit little-endian", + NULL, AV_CODEC_ID_PCM_S32LE) + +PCMDEF(s24be, "PCM signed 24-bit big-endian", + NULL, AV_CODEC_ID_PCM_S24BE) + +PCMDEF(s24le, "PCM signed 24-bit little-endian", + NULL, AV_CODEC_ID_PCM_S24LE) + +PCMDEF(s16be, "PCM signed 16-bit big-endian", + AV_NE("sw", NULL), AV_CODEC_ID_PCM_S16BE) + +PCMDEF(s16le, "PCM signed 16-bit little-endian", + AV_NE(NULL, "sw"), AV_CODEC_ID_PCM_S16LE) + +PCMDEF(s8, "PCM signed 8-bit", + "sb", AV_CODEC_ID_PCM_S8) + +PCMDEF(u32be, "PCM unsigned 32-bit big-endian", + NULL, AV_CODEC_ID_PCM_U32BE) + +PCMDEF(u32le, "PCM unsigned 32-bit little-endian", + NULL, AV_CODEC_ID_PCM_U32LE) + +PCMDEF(u24be, "PCM unsigned 24-bit big-endian", + NULL, AV_CODEC_ID_PCM_U24BE) + +PCMDEF(u24le, "PCM unsigned 24-bit little-endian", + NULL, AV_CODEC_ID_PCM_U24LE) + +PCMDEF(u16be, "PCM unsigned 16-bit big-endian", + AV_NE("uw", NULL), AV_CODEC_ID_PCM_U16BE) + +PCMDEF(u16le, "PCM unsigned 16-bit little-endian", + AV_NE(NULL, "uw"), AV_CODEC_ID_PCM_U16LE) + +PCMDEF(u8, "PCM unsigned 8-bit", + "ub", AV_CODEC_ID_PCM_U8) + +PCMDEF(alaw, "PCM A-law", + "al", AV_CODEC_ID_PCM_ALAW) + +PCMDEF(mulaw, "PCM mu-law", + "ul", AV_CODEC_ID_PCM_MULAW) diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pcmenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/pcmenc.o libavformat/pcmenc.o: libavformat/pcmenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pcmenc.o Binary file ffmpeg/libavformat/pcmenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pjsdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pjsdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * PJS (Phoenix Japanimation Society) subtitles format demuxer + * + * @see http://subs.com.ru/page.php?al=pjs + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} PJSContext; + +static int pjs_probe(AVProbeData *p) +{ + char c; + int64_t start, end; + const unsigned char *ptr = p->buf; + + if (sscanf(ptr, "%"SCNd64",%"SCNd64",%c", &start, &end, &c) == 3) { + size_t q1pos = strcspn(ptr, "\""); + size_t q2pos = q1pos + strcspn(ptr + q1pos + 1, "\"") + 1; + if (strcspn(ptr, "\r\n") > q2pos) + return AVPROBE_SCORE_MAX; + } + return 0; +} + +static int64_t read_ts(char **line, int *duration) +{ + int64_t start, end; + + if (sscanf(*line, "%"SCNd64",%"SCNd64, &start, &end) == 2) { + *line += strcspn(*line, "\"") + 1; + *duration = end - start; + return start; + } + return AV_NOPTS_VALUE; +} + +static int pjs_read_header(AVFormatContext *s) +{ + PJSContext *pjs = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + int res = 0; + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 10); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_PJS; + + while (!url_feof(s->pb)) { + char line[4096]; + char *p = line; + const int64_t pos = avio_tell(s->pb); + int len = ff_get_line(s->pb, line, sizeof(line)); + int64_t pts_start; + int duration; + + if (!len) + break; + + line[strcspn(line, "\r\n")] = 0; + + pts_start = read_ts(&p, &duration); + if (pts_start != AV_NOPTS_VALUE) { + AVPacket *sub; + + p[strcspn(p, "\"")] = 0; + sub = ff_subtitles_queue_insert(&pjs->q, p, strlen(p), 0); + if (!sub) + return AVERROR(ENOMEM); + sub->pos = pos; + sub->pts = pts_start; + sub->duration = duration; + } + } + + ff_subtitles_queue_finalize(&pjs->q); + return res; +} + +static int pjs_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PJSContext *pjs = s->priv_data; + return ff_subtitles_queue_read_packet(&pjs->q, pkt); +} + +static int pjs_read_seek(AVFormatContext *s, int stream_index, + int64_t min_ts, int64_t ts, int64_t max_ts, int flags) +{ + PJSContext *pjs = s->priv_data; + return ff_subtitles_queue_seek(&pjs->q, s, stream_index, + min_ts, ts, max_ts, flags); +} + +static int pjs_read_close(AVFormatContext *s) +{ + PJSContext *pjs = s->priv_data; + ff_subtitles_queue_clean(&pjs->q); + return 0; +} + +AVInputFormat ff_pjs_demuxer = { + .name = "pjs", + .long_name = NULL_IF_CONFIG_SMALL("PJS (Phoenix Japanimation Society) subtitles"), + .priv_data_size = sizeof(PJSContext), + .read_probe = pjs_probe, + .read_header = pjs_read_header, + .read_packet = pjs_read_packet, + .read_seek2 = pjs_read_seek, + .read_close = pjs_read_close, + .extensions = "pjs", +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pjsdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pjsdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/pjsdec.o libavformat/pjsdec.o: libavformat/pjsdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/subtitles.h libavutil/bprint.h libavutil/avstring.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pjsdec.o Binary file ffmpeg/libavformat/pjsdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pmpdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pmpdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,197 @@ +/* + * PMP demuxer. + * Copyright (c) 2011 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + int cur_stream; + int num_streams; + int audio_packets; + int current_packet; + uint32_t *packet_sizes; + int packet_sizes_alloc; +} PMPContext; + +static int pmp_probe(AVProbeData *p) { + if (AV_RN32(p->buf) == AV_RN32("pmpm") && + AV_RL32(p->buf + 4) == 1) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int pmp_header(AVFormatContext *s) +{ + PMPContext *pmp = s->priv_data; + AVIOContext *pb = s->pb; + int tb_num, tb_den; + uint32_t index_cnt; + int audio_codec_id = AV_CODEC_ID_NONE; + int srate, channels; + unsigned i; + uint64_t pos; + int64_t fsize = avio_size(pb); + + AVStream *vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; + avio_skip(pb, 8); + switch (avio_rl32(pb)) { + case 0: + vst->codec->codec_id = AV_CODEC_ID_MPEG4; + break; + case 1: + vst->codec->codec_id = AV_CODEC_ID_H264; + break; + default: + av_log(s, AV_LOG_ERROR, "Unsupported video format\n"); + break; + } + index_cnt = avio_rl32(pb); + vst->codec->width = avio_rl32(pb); + vst->codec->height = avio_rl32(pb); + + tb_num = avio_rl32(pb); + tb_den = avio_rl32(pb); + avpriv_set_pts_info(vst, 32, tb_num, tb_den); + vst->nb_frames = index_cnt; + vst->duration = index_cnt; + + switch (avio_rl32(pb)) { + case 0: + audio_codec_id = AV_CODEC_ID_MP3; + break; + case 1: + av_log(s, AV_LOG_ERROR, "AAC not yet correctly supported\n"); + audio_codec_id = AV_CODEC_ID_AAC; + break; + default: + av_log(s, AV_LOG_ERROR, "Unsupported audio format\n"); + break; + } + pmp->num_streams = avio_rl16(pb) + 1; + avio_skip(pb, 10); + srate = avio_rl32(pb); + channels = avio_rl32(pb) + 1; + pos = avio_tell(pb) + 4LL*index_cnt; + for (i = 0; i < index_cnt; i++) { + uint32_t size = avio_rl32(pb); + int flags = size & 1 ? AVINDEX_KEYFRAME : 0; + if (url_feof(pb)) { + av_log(s, AV_LOG_FATAL, "Encountered EOF while reading index.\n"); + return AVERROR_INVALIDDATA; + } + size >>= 1; + if (size < 9 + 4*pmp->num_streams) { + av_log(s, AV_LOG_ERROR, "Packet too small\n"); + return AVERROR_INVALIDDATA; + } + av_add_index_entry(vst, pos, i, size, 0, flags); + pos += size; + if (fsize > 0 && i == 0 && pos > fsize) { + av_log(s, AV_LOG_ERROR, "File ends before first packet\n"); + return AVERROR_INVALIDDATA; + } + } + for (i = 1; i < pmp->num_streams; i++) { + AVStream *ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = audio_codec_id; + ast->codec->channels = channels; + ast->codec->sample_rate = srate; + avpriv_set_pts_info(ast, 32, 1, srate); + } + return 0; +} + +static int pmp_packet(AVFormatContext *s, AVPacket *pkt) +{ + PMPContext *pmp = s->priv_data; + AVIOContext *pb = s->pb; + int ret = 0; + int i; + + if (url_feof(pb)) + return AVERROR_EOF; + if (pmp->cur_stream == 0) { + int num_packets; + pmp->audio_packets = avio_r8(pb); + if (!pmp->audio_packets) { + avpriv_request_sample(s, "0 audio packets"); + return AVERROR_PATCHWELCOME; + } + num_packets = (pmp->num_streams - 1) * pmp->audio_packets + 1; + avio_skip(pb, 8); + pmp->current_packet = 0; + av_fast_malloc(&pmp->packet_sizes, + &pmp->packet_sizes_alloc, + num_packets * sizeof(*pmp->packet_sizes)); + if (!pmp->packet_sizes_alloc) { + av_log(s, AV_LOG_ERROR, "Cannot (re)allocate packet buffer\n"); + return AVERROR(ENOMEM); + } + for (i = 0; i < num_packets; i++) + pmp->packet_sizes[i] = avio_rl32(pb); + } + ret = av_get_packet(pb, pkt, pmp->packet_sizes[pmp->current_packet]); + if (ret >= 0) { + ret = 0; + // FIXME: this is a hack that should be removed once + // compute_pkt_fields() can handle timestamps properly + if (pmp->cur_stream == 0) + pkt->dts = s->streams[0]->cur_dts++; + pkt->stream_index = pmp->cur_stream; + } + if (pmp->current_packet % pmp->audio_packets == 0) + pmp->cur_stream = (pmp->cur_stream + 1) % pmp->num_streams; + pmp->current_packet++; + return ret; +} + +static int pmp_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags) +{ + PMPContext *pmp = s->priv_data; + pmp->cur_stream = 0; + // fallback to default seek now + return -1; +} + +static int pmp_close(AVFormatContext *s) +{ + PMPContext *pmp = s->priv_data; + av_freep(&pmp->packet_sizes); + return 0; +} + +AVInputFormat ff_pmp_demuxer = { + .name = "pmp", + .long_name = NULL_IF_CONFIG_SMALL("Playstation Portable PMP"), + .priv_data_size = sizeof(PMPContext), + .read_probe = pmp_probe, + .read_header = pmp_header, + .read_packet = pmp_packet, + .read_seek = pmp_seek, + .read_close = pmp_close, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pmpdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pmpdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/pmpdec.o libavformat/pmpdec.o: libavformat/pmpdec.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/rational.h libavutil/intfloat_readwrite.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/avutil.h libavutil/buffer.h libavutil/cpu.h \ + libavutil/channel_layout.h libavutil/dict.h libavutil/frame.h \ + libavcodec/version.h libavutil/buffer.h libavutil/samplefmt.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pmpdec.o Binary file ffmpeg/libavformat/pmpdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/psxstr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/psxstr.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,317 @@ +/* + * Sony Playstation (PSX) STR File Demuxer + * Copyright (c) 2003 The ffmpeg Project + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * PSX STR file demuxer + * by Mike Melanson (melanson@pcisys.net) + * This module handles streams that have been ripped from Sony Playstation + * CD games. This demuxer can handle either raw STR files (which are just + * concatenations of raw compact disc sectors) or STR files with 0x2C-byte + * RIFF headers, followed by CD sectors. + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define RIFF_TAG MKTAG('R', 'I', 'F', 'F') +#define CDXA_TAG MKTAG('C', 'D', 'X', 'A') + +#define RAW_CD_SECTOR_SIZE 2352 +#define RAW_CD_SECTOR_DATA_SIZE 2304 +#define VIDEO_DATA_CHUNK_SIZE 0x7E0 +#define VIDEO_DATA_HEADER_SIZE 0x38 +#define RIFF_HEADER_SIZE 0x2C + +#define CDXA_TYPE_MASK 0x0E +#define CDXA_TYPE_DATA 0x08 +#define CDXA_TYPE_AUDIO 0x04 +#define CDXA_TYPE_VIDEO 0x02 + +#define STR_MAGIC (0x80010160) + +typedef struct StrChannel { + /* video parameters */ + int video_stream_index; + AVPacket tmp_pkt; + + /* audio parameters */ + int audio_stream_index; +} StrChannel; + +typedef struct StrDemuxContext { + + /* a STR file can contain up to 32 channels of data */ + StrChannel channels[32]; +} StrDemuxContext; + +static const uint8_t sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00}; + +static int str_probe(AVProbeData *p) +{ + const uint8_t *sector= p->buf; + const uint8_t *end= sector + p->buf_size; + int aud=0, vid=0; + + if (p->buf_size < RAW_CD_SECTOR_SIZE) + return 0; + + if ((AV_RL32(&p->buf[0]) == RIFF_TAG) && + (AV_RL32(&p->buf[8]) == CDXA_TAG)) { + + /* RIFF header seen; skip 0x2C bytes */ + sector += RIFF_HEADER_SIZE; + } + + while (end - sector >= RAW_CD_SECTOR_SIZE) { + /* look for CD sync header (00, 0xFF x 10, 00) */ + if (memcmp(sector,sync_header,sizeof(sync_header))) + return 0; + + if (sector[0x11] >= 32) + return 0; + + switch (sector[0x12] & CDXA_TYPE_MASK) { + case CDXA_TYPE_DATA: + case CDXA_TYPE_VIDEO: { + int current_sector = AV_RL16(§or[0x1C]); + int sector_count = AV_RL16(§or[0x1E]); + int frame_size = AV_RL32(§or[0x24]); + + if(!( frame_size>=0 + && current_sector < sector_count + && sector_count*VIDEO_DATA_CHUNK_SIZE >=frame_size)){ + return 0; + } + + /*st->codec->width = AV_RL16(§or[0x28]); + st->codec->height = AV_RL16(§or[0x2A]);*/ + +// if (current_sector == sector_count-1) { + vid++; +// } + + } + break; + case CDXA_TYPE_AUDIO: + if(sector[0x13]&0x2A) + return 0; + aud++; + break; + default: + if(sector[0x12] & CDXA_TYPE_MASK) + return 0; + } + sector += RAW_CD_SECTOR_SIZE; + } + /* MPEG files (like those ripped from VCDs) can also look like this; + * only return half certainty */ + if(vid+aud > 3) return 50; + else if(vid+aud) return 1; + else return 0; +} + +static int str_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + StrDemuxContext *str = s->priv_data; + unsigned char sector[RAW_CD_SECTOR_SIZE]; + int start; + int i; + + /* skip over any RIFF header */ + if (avio_read(pb, sector, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) + return AVERROR(EIO); + if (AV_RL32(§or[0]) == RIFF_TAG) + start = RIFF_HEADER_SIZE; + else + start = 0; + + avio_seek(pb, start, SEEK_SET); + + for(i=0; i<32; i++){ + str->channels[i].video_stream_index= + str->channels[i].audio_stream_index= -1; + } + + s->ctx_flags |= AVFMTCTX_NOHEADER; + + return 0; +} + +static int str_read_packet(AVFormatContext *s, + AVPacket *ret_pkt) +{ + AVIOContext *pb = s->pb; + StrDemuxContext *str = s->priv_data; + unsigned char sector[RAW_CD_SECTOR_SIZE]; + int channel; + AVPacket *pkt; + AVStream *st; + + while (1) { + + if (avio_read(pb, sector, RAW_CD_SECTOR_SIZE) != RAW_CD_SECTOR_SIZE) + return AVERROR(EIO); + + channel = sector[0x11]; + if (channel >= 32) + return AVERROR_INVALIDDATA; + + switch (sector[0x12] & CDXA_TYPE_MASK) { + + case CDXA_TYPE_DATA: + case CDXA_TYPE_VIDEO: + { + + int current_sector = AV_RL16(§or[0x1C]); + int sector_count = AV_RL16(§or[0x1E]); + int frame_size = AV_RL32(§or[0x24]); + + if(!( frame_size>=0 + && current_sector < sector_count + && sector_count*VIDEO_DATA_CHUNK_SIZE >=frame_size)){ + av_log(s, AV_LOG_ERROR, "Invalid parameters %d %d %d\n", current_sector, sector_count, frame_size); + break; + } + + if(str->channels[channel].video_stream_index < 0){ + /* allocate a new AVStream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 15); + + str->channels[channel].video_stream_index = st->index; + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MDEC; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = AV_RL16(§or[0x28]); + st->codec->height = AV_RL16(§or[0x2A]); + } + + /* if this is the first sector of the frame, allocate a pkt */ + pkt = &str->channels[channel].tmp_pkt; + + if(pkt->size != sector_count*VIDEO_DATA_CHUNK_SIZE){ + if(pkt->data) + av_log(s, AV_LOG_ERROR, "missmatching sector_count\n"); + av_free_packet(pkt); + if (av_new_packet(pkt, sector_count*VIDEO_DATA_CHUNK_SIZE)) + return AVERROR(EIO); + + pkt->pos= avio_tell(pb) - RAW_CD_SECTOR_SIZE; + pkt->stream_index = + str->channels[channel].video_stream_index; + } + + memcpy(pkt->data + current_sector*VIDEO_DATA_CHUNK_SIZE, + sector + VIDEO_DATA_HEADER_SIZE, + VIDEO_DATA_CHUNK_SIZE); + + if (current_sector == sector_count-1) { + pkt->size= frame_size; + *ret_pkt = *pkt; + pkt->data= NULL; + pkt->size= -1; + pkt->buf = NULL; +#if FF_API_DESTRUCT_PACKET + pkt->destruct = NULL; +#endif + return 0; + } + + } + break; + + case CDXA_TYPE_AUDIO: + if(str->channels[channel].audio_stream_index < 0){ + int fmt = sector[0x13]; + /* allocate a new AVStream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + str->channels[channel].audio_stream_index = st->index; + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_ADPCM_XA; + st->codec->codec_tag = 0; /* no fourcc */ + if (fmt & 1) { + st->codec->channels = 2; + st->codec->channel_layout = AV_CH_LAYOUT_STEREO; + } else { + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + } + st->codec->sample_rate = (fmt&4)?18900:37800; + // st->codec->bit_rate = 0; //FIXME; + st->codec->block_align = 128; + + avpriv_set_pts_info(st, 64, 18 * 224 / st->codec->channels, + st->codec->sample_rate); + st->start_time = 0; + } + pkt = ret_pkt; + if (av_new_packet(pkt, 2304)) + return AVERROR(EIO); + memcpy(pkt->data,sector+24,2304); + + pkt->stream_index = + str->channels[channel].audio_stream_index; + pkt->duration = 1; + return 0; + default: + av_log(s, AV_LOG_WARNING, "Unknown sector type %02X\n", sector[0x12]); + /* drop the sector and move on */ + break; + } + + if (url_feof(pb)) + return AVERROR(EIO); + } +} + +static int str_read_close(AVFormatContext *s) +{ + StrDemuxContext *str = s->priv_data; + int i; + for(i=0; i<32; i++){ + if(str->channels[i].tmp_pkt.data) + av_free_packet(&str->channels[i].tmp_pkt); + } + + return 0; +} + +AVInputFormat ff_str_demuxer = { + .name = "psxstr", + .long_name = NULL_IF_CONFIG_SMALL("Sony Playstation STR"), + .priv_data_size = sizeof(StrDemuxContext), + .read_probe = str_probe, + .read_header = str_read_header, + .read_packet = str_read_packet, + .read_close = str_read_close, + .flags = AVFMT_NO_BYTE_SEEK, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/psxstr.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/psxstr.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/psxstr.o libavformat/psxstr.o: libavformat/psxstr.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/psxstr.o Binary file ffmpeg/libavformat/psxstr.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pva.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pva.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,225 @@ +/* + * TechnoTrend PVA (.pva) demuxer + * Copyright (c) 2007, 2008 Ivo van Poorten + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "mpeg.h" + +#define PVA_MAX_PAYLOAD_LENGTH 0x17f8 +#define PVA_VIDEO_PAYLOAD 0x01 +#define PVA_AUDIO_PAYLOAD 0x02 +#define PVA_MAGIC (('A' << 8) + 'V') + +typedef struct { + int continue_pes; +} PVAContext; + +static int pva_check(const uint8_t *p) { + int length = AV_RB16(p + 6); + if (AV_RB16(p) != PVA_MAGIC || !p[2] || p[2] > 2 || p[4] != 0x55 || + (p[5] & 0xe0) || length > PVA_MAX_PAYLOAD_LENGTH) + return -1; + return length + 8; +} + +static int pva_probe(AVProbeData * pd) { + const unsigned char *buf = pd->buf; + int len = pva_check(buf); + + if (len < 0) + return 0; + + if (pd->buf_size >= len + 8 && + pva_check(buf + len) >= 0) + return AVPROBE_SCORE_MAX / 2; + + return AVPROBE_SCORE_MAX / 4; +} + +static int pva_read_header(AVFormatContext *s) { + AVStream *st; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO; + st->need_parsing = AVSTREAM_PARSE_FULL; + avpriv_set_pts_info(st, 32, 1, 90000); + av_add_index_entry(st, 0, 0, 0, 0, AVINDEX_KEYFRAME); + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_MP2; + st->need_parsing = AVSTREAM_PARSE_FULL; + avpriv_set_pts_info(st, 33, 1, 90000); + av_add_index_entry(st, 0, 0, 0, 0, AVINDEX_KEYFRAME); + + /* the parameters will be extracted from the compressed bitstream */ + return 0; +} + +#define pva_log if (read_packet) av_log + +static int read_part_of_packet(AVFormatContext *s, int64_t *pts, + int *len, int *strid, int read_packet) { + AVIOContext *pb = s->pb; + PVAContext *pvactx = s->priv_data; + int syncword, streamid, reserved, flags, length, pts_flag; + int64_t pva_pts = AV_NOPTS_VALUE, startpos; + +recover: + startpos = avio_tell(pb); + + syncword = avio_rb16(pb); + streamid = avio_r8(pb); + avio_r8(pb); /* counter not used */ + reserved = avio_r8(pb); + flags = avio_r8(pb); + length = avio_rb16(pb); + + pts_flag = flags & 0x10; + + if (syncword != PVA_MAGIC) { + pva_log(s, AV_LOG_ERROR, "invalid syncword\n"); + return AVERROR(EIO); + } + if (streamid != PVA_VIDEO_PAYLOAD && streamid != PVA_AUDIO_PAYLOAD) { + pva_log(s, AV_LOG_ERROR, "invalid streamid\n"); + return AVERROR(EIO); + } + if (reserved != 0x55) { + pva_log(s, AV_LOG_WARNING, "expected reserved byte to be 0x55\n"); + } + if (length > PVA_MAX_PAYLOAD_LENGTH) { + pva_log(s, AV_LOG_ERROR, "invalid payload length %u\n", length); + return AVERROR(EIO); + } + + if (streamid == PVA_VIDEO_PAYLOAD && pts_flag) { + pva_pts = avio_rb32(pb); + length -= 4; + } else if (streamid == PVA_AUDIO_PAYLOAD) { + /* PVA Audio Packets either start with a signaled PES packet or + * are a continuation of the previous PES packet. New PES packets + * always start at the beginning of a PVA Packet, never somewhere in + * the middle. */ + if (!pvactx->continue_pes) { + int pes_signal, pes_header_data_length, pes_packet_length, + pes_flags; + unsigned char pes_header_data[256]; + + pes_signal = avio_rb24(pb); + avio_r8(pb); + pes_packet_length = avio_rb16(pb); + pes_flags = avio_rb16(pb); + pes_header_data_length = avio_r8(pb); + + if (pes_signal != 1) { + pva_log(s, AV_LOG_WARNING, "expected signaled PES packet, " + "trying to recover\n"); + avio_skip(pb, length - 9); + if (!read_packet) + return AVERROR(EIO); + goto recover; + } + + avio_read(pb, pes_header_data, pes_header_data_length); + length -= 9 + pes_header_data_length; + + pes_packet_length -= 3 + pes_header_data_length; + + pvactx->continue_pes = pes_packet_length; + + if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20) + pva_pts = ff_parse_pes_pts(pes_header_data); + } + + pvactx->continue_pes -= length; + + if (pvactx->continue_pes < 0) { + pva_log(s, AV_LOG_WARNING, "audio data corruption\n"); + pvactx->continue_pes = 0; + } + } + + if (pva_pts != AV_NOPTS_VALUE) + av_add_index_entry(s->streams[streamid-1], startpos, pva_pts, 0, 0, AVINDEX_KEYFRAME); + + *pts = pva_pts; + *len = length; + *strid = streamid; + return 0; +} + +static int pva_read_packet(AVFormatContext *s, AVPacket *pkt) { + AVIOContext *pb = s->pb; + int64_t pva_pts; + int ret, length, streamid; + + if (read_part_of_packet(s, &pva_pts, &length, &streamid, 1) < 0 || + (ret = av_get_packet(pb, pkt, length)) <= 0) + return AVERROR(EIO); + + pkt->stream_index = streamid - 1; + pkt->pts = pva_pts; + + return ret; +} + +static int64_t pva_read_timestamp(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit) { + AVIOContext *pb = s->pb; + PVAContext *pvactx = s->priv_data; + int length, streamid; + int64_t res = AV_NOPTS_VALUE; + + pos_limit = FFMIN(*pos+PVA_MAX_PAYLOAD_LENGTH*8, (uint64_t)*pos+pos_limit); + + while (*pos < pos_limit) { + res = AV_NOPTS_VALUE; + avio_seek(pb, *pos, SEEK_SET); + + pvactx->continue_pes = 0; + if (read_part_of_packet(s, &res, &length, &streamid, 0)) { + (*pos)++; + continue; + } + if (streamid - 1 != stream_index || res == AV_NOPTS_VALUE) { + *pos = avio_tell(pb) + length; + continue; + } + break; + } + + pvactx->continue_pes = 0; + return res; +} + +AVInputFormat ff_pva_demuxer = { + .name = "pva", + .long_name = NULL_IF_CONFIG_SMALL("TechnoTrend PVA"), + .priv_data_size = sizeof(PVAContext), + .read_probe = pva_probe, + .read_header = pva_read_header, + .read_packet = pva_read_packet, + .read_timestamp = pva_read_timestamp, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pva.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pva.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/pva.o libavformat/pva.o: libavformat/pva.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/mpeg.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pva.o Binary file ffmpeg/libavformat/pva.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pvfdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pvfdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,75 @@ +/* + * PVF demuxer + * Copyright (c) 2012 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int pvf_probe(AVProbeData *p) +{ + if (!memcmp(p->buf, "PVF1\n", 5)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int pvf_read_header(AVFormatContext *s) +{ + char buffer[32]; + AVStream *st; + int bps, channels, sample_rate; + + avio_skip(s->pb, 5); + ff_get_line(s->pb, buffer, sizeof(buffer)); + if (sscanf(buffer, "%d %d %d", + &channels, + &sample_rate, + &bps) != 3) + return AVERROR_INVALIDDATA; + + if (channels <= 0 || bps <= 0 || sample_rate <= 0) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = channels; + st->codec->sample_rate = sample_rate; + st->codec->codec_id = ff_get_pcm_codec_id(bps, 0, 1, 0xFFFF); + st->codec->bits_per_coded_sample = bps; + st->codec->block_align = bps * st->codec->channels / 8; + + avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); + + return 0; +} + +AVInputFormat ff_pvf_demuxer = { + .name = "pvf", + .long_name = NULL_IF_CONFIG_SMALL("PVF (Portable Voice Format)"), + .read_probe = pvf_probe, + .read_header = pvf_read_header, + .read_packet = ff_pcm_read_packet, + .read_seek = ff_pcm_read_seek, + .extensions = "pvf", + .flags = AVFMT_GENERIC_INDEX, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pvfdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/pvfdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/pvfdec.o libavformat/pvfdec.o: libavformat/pvfdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/pcm.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/pvfdec.o Binary file ffmpeg/libavformat/pvfdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/qcp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/qcp.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,196 @@ +/* + * QCP format (.qcp) demuxer + * Copyright (c) 2009 Kenan Gillet + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * QCP format (.qcp) demuxer + * @author Kenan Gillet + * @see RFC 3625: "The QCP File Format and Media Types for Speech Data" + * http://tools.ietf.org/html/rfc3625 + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +typedef struct { + uint32_t data_size; ///< size of data chunk + +#define QCP_MAX_MODE 4 + int16_t rates_per_mode[QCP_MAX_MODE+1]; ///< contains the packet size corresponding + ///< to each mode, -1 if no size. +} QCPContext; + +/** + * Last 15 out of 16 bytes of QCELP-13K GUID, as stored in the file; + * the first byte of the GUID can be either 0x41 or 0x42. + */ +static const uint8_t guid_qcelp_13k_part[15] = { + 0x6d, 0x7f, 0x5e, 0x15, 0xb1, 0xd0, 0x11, 0xba, + 0x91, 0x00, 0x80, 0x5f, 0xb4, 0xb9, 0x7e +}; + +/** + * EVRC GUID as stored in the file + */ +static const uint8_t guid_evrc[16] = { + 0x8d, 0xd4, 0x89, 0xe6, 0x76, 0x90, 0xb5, 0x46, + 0x91, 0xef, 0x73, 0x6a, 0x51, 0x00, 0xce, 0xb4 +}; + +/** + * SMV GUID as stored in the file + */ +static const uint8_t guid_smv[16] = { + 0x75, 0x2b, 0x7c, 0x8d, 0x97, 0xa7, 0x49, 0xed, + 0x98, 0x5e, 0xd5, 0x3c, 0x8c, 0xc7, 0x5f, 0x84 +}; + +/** + * @param guid contains at least 16 bytes + * @return 1 if the guid is a qcelp_13k guid, 0 otherwise + */ +static int is_qcelp_13k_guid(const uint8_t *guid) { + return (guid[0] == 0x41 || guid[0] == 0x42) + && !memcmp(guid+1, guid_qcelp_13k_part, sizeof(guid_qcelp_13k_part)); +} + +static int qcp_probe(AVProbeData *pd) +{ + if (AV_RL32(pd->buf ) == AV_RL32("RIFF") && + AV_RL64(pd->buf+8) == AV_RL64("QLCMfmt ")) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int qcp_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + QCPContext *c = s->priv_data; + AVStream *st = avformat_new_stream(s, NULL); + uint8_t buf[16]; + int i, nb_rates; + + if (!st) + return AVERROR(ENOMEM); + + avio_rb32(pb); // "RIFF" + avio_skip(pb, 4 + 8 + 4 + 1 + 1); // filesize + "QLCMfmt " + chunk-size + major-version + minor-version + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + avio_read(pb, buf, 16); + if (is_qcelp_13k_guid(buf)) { + st->codec->codec_id = AV_CODEC_ID_QCELP; + } else if (!memcmp(buf, guid_evrc, 16)) { + st->codec->codec_id = AV_CODEC_ID_EVRC; + } else if (!memcmp(buf, guid_smv, 16)) { + st->codec->codec_id = AV_CODEC_ID_SMV; + } else { + av_log(s, AV_LOG_ERROR, "Unknown codec GUID.\n"); + return AVERROR_INVALIDDATA; + } + avio_skip(pb, 2 + 80); // codec-version + codec-name + st->codec->bit_rate = avio_rl16(pb); + + s->packet_size = avio_rl16(pb); + avio_skip(pb, 2); // block-size + st->codec->sample_rate = avio_rl16(pb); + avio_skip(pb, 2); // sample-size + + memset(c->rates_per_mode, -1, sizeof(c->rates_per_mode)); + nb_rates = avio_rl32(pb); + nb_rates = FFMIN(nb_rates, 8); + for (i=0; i QCP_MAX_MODE) { + av_log(s, AV_LOG_WARNING, "Unknown entry %d=>%d in rate-map-table.\n ", mode, size); + } else + c->rates_per_mode[mode] = size; + } + avio_skip(pb, 16 - 2*nb_rates + 20); // empty entries of rate-map-table + reserved + + return 0; +} + +static int qcp_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + QCPContext *c = s->priv_data; + unsigned int chunk_size, tag; + + while(!url_feof(pb)) { + if (c->data_size) { + int pkt_size, ret, mode = avio_r8(pb); + + if (s->packet_size) { + pkt_size = s->packet_size - 1; + } else if (mode > QCP_MAX_MODE || (pkt_size = c->rates_per_mode[mode]) < 0) { + c->data_size--; + continue; + } + + if (c->data_size <= pkt_size) { + av_log(s, AV_LOG_WARNING, "Data chunk is too small.\n"); + pkt_size = c->data_size - 1; + } + + if ((ret = av_get_packet(pb, pkt, pkt_size)) >= 0) { + if (pkt_size != ret) + av_log(s, AV_LOG_ERROR, "Packet size is too small.\n"); + + c->data_size -= pkt_size + 1; + } + return ret; + } + + if (avio_tell(pb) & 1 && avio_r8(pb)) + av_log(s, AV_LOG_WARNING, "Padding should be 0.\n"); + + tag = avio_rl32(pb); + chunk_size = avio_rl32(pb); + switch (tag) { + case MKTAG('v', 'r', 'a', 't'): + if (avio_rl32(pb)) // var-rate-flag + s->packet_size = 0; + avio_skip(pb, 4); // size-in-packets + break; + case MKTAG('d', 'a', 't', 'a'): + c->data_size = chunk_size; + break; + + default: + avio_skip(pb, chunk_size); + } + } + return AVERROR_EOF; +} + +AVInputFormat ff_qcp_demuxer = { + .name = "qcp", + .long_name = NULL_IF_CONFIG_SMALL("QCP"), + .priv_data_size = sizeof(QCPContext), + .read_probe = qcp_probe, + .read_header = qcp_read_header, + .read_packet = qcp_read_packet, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/qcp.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/qcp.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/qcp.o libavformat/qcp.o: libavformat/qcp.c \ + libavutil/channel_layout.h libavutil/intreadwrite.h \ + libavutil/avconfig.h libavutil/attributes.h libavutil/bswap.h config.h \ + libavutil/x86/bswap.h config.h libavutil/attributes.h \ + libavutil/x86/intreadwrite.h libavformat/avformat.h \ + libavcodec/avcodec.h libavutil/samplefmt.h libavutil/avutil.h \ + libavutil/common.h libavutil/version.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h libavutil/libm.h \ + libavutil/intfloat.h libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/qcp.o Binary file ffmpeg/libavformat/qcp.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/qtpalette.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/qtpalette.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,313 @@ +/* + * Default Palettes for Quicktime Files + * Automatically generated from a utility derived from XAnim: + * http://xanim.va.pubnix.com/home.html + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_QTPALETTE_H +#define AVFORMAT_QTPALETTE_H + +#include + +static const uint8_t ff_qt_default_palette_4[4 * 3] = { + 0x93, 0x65, 0x5E, + 0xFF, 0xFF, 0xFF, + 0xDF, 0xD0, 0xAB, + 0x00, 0x00, 0x00 +}; + +static const uint8_t ff_qt_default_palette_16[16 * 3] = { + 0xFF, 0xFB, 0xFF, + 0xEF, 0xD9, 0xBB, + 0xE8, 0xC9, 0xB1, + 0x93, 0x65, 0x5E, + 0xFC, 0xDE, 0xE8, + 0x9D, 0x88, 0x91, + 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, + 0x47, 0x48, 0x37, + 0x7A, 0x5E, 0x55, + 0xDF, 0xD0, 0xAB, + 0xFF, 0xFB, 0xF9, + 0xE8, 0xCA, 0xC5, + 0x8A, 0x7C, 0x77, + 0x00, 0x00, 0x00 +}; + +static const uint8_t ff_qt_default_palette_256[256 * 3] = { + /* 0, 0x00 */ 0xFF, 0xFF, 0xFF, + /* 1, 0x01 */ 0xFF, 0xFF, 0xCC, + /* 2, 0x02 */ 0xFF, 0xFF, 0x99, + /* 3, 0x03 */ 0xFF, 0xFF, 0x66, + /* 4, 0x04 */ 0xFF, 0xFF, 0x33, + /* 5, 0x05 */ 0xFF, 0xFF, 0x00, + /* 6, 0x06 */ 0xFF, 0xCC, 0xFF, + /* 7, 0x07 */ 0xFF, 0xCC, 0xCC, + /* 8, 0x08 */ 0xFF, 0xCC, 0x99, + /* 9, 0x09 */ 0xFF, 0xCC, 0x66, + /* 10, 0x0A */ 0xFF, 0xCC, 0x33, + /* 11, 0x0B */ 0xFF, 0xCC, 0x00, + /* 12, 0x0C */ 0xFF, 0x99, 0xFF, + /* 13, 0x0D */ 0xFF, 0x99, 0xCC, + /* 14, 0x0E */ 0xFF, 0x99, 0x99, + /* 15, 0x0F */ 0xFF, 0x99, 0x66, + /* 16, 0x10 */ 0xFF, 0x99, 0x33, + /* 17, 0x11 */ 0xFF, 0x99, 0x00, + /* 18, 0x12 */ 0xFF, 0x66, 0xFF, + /* 19, 0x13 */ 0xFF, 0x66, 0xCC, + /* 20, 0x14 */ 0xFF, 0x66, 0x99, + /* 21, 0x15 */ 0xFF, 0x66, 0x66, + /* 22, 0x16 */ 0xFF, 0x66, 0x33, + /* 23, 0x17 */ 0xFF, 0x66, 0x00, + /* 24, 0x18 */ 0xFF, 0x33, 0xFF, + /* 25, 0x19 */ 0xFF, 0x33, 0xCC, + /* 26, 0x1A */ 0xFF, 0x33, 0x99, + /* 27, 0x1B */ 0xFF, 0x33, 0x66, + /* 28, 0x1C */ 0xFF, 0x33, 0x33, + /* 29, 0x1D */ 0xFF, 0x33, 0x00, + /* 30, 0x1E */ 0xFF, 0x00, 0xFF, + /* 31, 0x1F */ 0xFF, 0x00, 0xCC, + /* 32, 0x20 */ 0xFF, 0x00, 0x99, + /* 33, 0x21 */ 0xFF, 0x00, 0x66, + /* 34, 0x22 */ 0xFF, 0x00, 0x33, + /* 35, 0x23 */ 0xFF, 0x00, 0x00, + /* 36, 0x24 */ 0xCC, 0xFF, 0xFF, + /* 37, 0x25 */ 0xCC, 0xFF, 0xCC, + /* 38, 0x26 */ 0xCC, 0xFF, 0x99, + /* 39, 0x27 */ 0xCC, 0xFF, 0x66, + /* 40, 0x28 */ 0xCC, 0xFF, 0x33, + /* 41, 0x29 */ 0xCC, 0xFF, 0x00, + /* 42, 0x2A */ 0xCC, 0xCC, 0xFF, + /* 43, 0x2B */ 0xCC, 0xCC, 0xCC, + /* 44, 0x2C */ 0xCC, 0xCC, 0x99, + /* 45, 0x2D */ 0xCC, 0xCC, 0x66, + /* 46, 0x2E */ 0xCC, 0xCC, 0x33, + /* 47, 0x2F */ 0xCC, 0xCC, 0x00, + /* 48, 0x30 */ 0xCC, 0x99, 0xFF, + /* 49, 0x31 */ 0xCC, 0x99, 0xCC, + /* 50, 0x32 */ 0xCC, 0x99, 0x99, + /* 51, 0x33 */ 0xCC, 0x99, 0x66, + /* 52, 0x34 */ 0xCC, 0x99, 0x33, + /* 53, 0x35 */ 0xCC, 0x99, 0x00, + /* 54, 0x36 */ 0xCC, 0x66, 0xFF, + /* 55, 0x37 */ 0xCC, 0x66, 0xCC, + /* 56, 0x38 */ 0xCC, 0x66, 0x99, + /* 57, 0x39 */ 0xCC, 0x66, 0x66, + /* 58, 0x3A */ 0xCC, 0x66, 0x33, + /* 59, 0x3B */ 0xCC, 0x66, 0x00, + /* 60, 0x3C */ 0xCC, 0x33, 0xFF, + /* 61, 0x3D */ 0xCC, 0x33, 0xCC, + /* 62, 0x3E */ 0xCC, 0x33, 0x99, + /* 63, 0x3F */ 0xCC, 0x33, 0x66, + /* 64, 0x40 */ 0xCC, 0x33, 0x33, + /* 65, 0x41 */ 0xCC, 0x33, 0x00, + /* 66, 0x42 */ 0xCC, 0x00, 0xFF, + /* 67, 0x43 */ 0xCC, 0x00, 0xCC, + /* 68, 0x44 */ 0xCC, 0x00, 0x99, + /* 69, 0x45 */ 0xCC, 0x00, 0x66, + /* 70, 0x46 */ 0xCC, 0x00, 0x33, + /* 71, 0x47 */ 0xCC, 0x00, 0x00, + /* 72, 0x48 */ 0x99, 0xFF, 0xFF, + /* 73, 0x49 */ 0x99, 0xFF, 0xCC, + /* 74, 0x4A */ 0x99, 0xFF, 0x99, + /* 75, 0x4B */ 0x99, 0xFF, 0x66, + /* 76, 0x4C */ 0x99, 0xFF, 0x33, + /* 77, 0x4D */ 0x99, 0xFF, 0x00, + /* 78, 0x4E */ 0x99, 0xCC, 0xFF, + /* 79, 0x4F */ 0x99, 0xCC, 0xCC, + /* 80, 0x50 */ 0x99, 0xCC, 0x99, + /* 81, 0x51 */ 0x99, 0xCC, 0x66, + /* 82, 0x52 */ 0x99, 0xCC, 0x33, + /* 83, 0x53 */ 0x99, 0xCC, 0x00, + /* 84, 0x54 */ 0x99, 0x99, 0xFF, + /* 85, 0x55 */ 0x99, 0x99, 0xCC, + /* 86, 0x56 */ 0x99, 0x99, 0x99, + /* 87, 0x57 */ 0x99, 0x99, 0x66, + /* 88, 0x58 */ 0x99, 0x99, 0x33, + /* 89, 0x59 */ 0x99, 0x99, 0x00, + /* 90, 0x5A */ 0x99, 0x66, 0xFF, + /* 91, 0x5B */ 0x99, 0x66, 0xCC, + /* 92, 0x5C */ 0x99, 0x66, 0x99, + /* 93, 0x5D */ 0x99, 0x66, 0x66, + /* 94, 0x5E */ 0x99, 0x66, 0x33, + /* 95, 0x5F */ 0x99, 0x66, 0x00, + /* 96, 0x60 */ 0x99, 0x33, 0xFF, + /* 97, 0x61 */ 0x99, 0x33, 0xCC, + /* 98, 0x62 */ 0x99, 0x33, 0x99, + /* 99, 0x63 */ 0x99, 0x33, 0x66, + /* 100, 0x64 */ 0x99, 0x33, 0x33, + /* 101, 0x65 */ 0x99, 0x33, 0x00, + /* 102, 0x66 */ 0x99, 0x00, 0xFF, + /* 103, 0x67 */ 0x99, 0x00, 0xCC, + /* 104, 0x68 */ 0x99, 0x00, 0x99, + /* 105, 0x69 */ 0x99, 0x00, 0x66, + /* 106, 0x6A */ 0x99, 0x00, 0x33, + /* 107, 0x6B */ 0x99, 0x00, 0x00, + /* 108, 0x6C */ 0x66, 0xFF, 0xFF, + /* 109, 0x6D */ 0x66, 0xFF, 0xCC, + /* 110, 0x6E */ 0x66, 0xFF, 0x99, + /* 111, 0x6F */ 0x66, 0xFF, 0x66, + /* 112, 0x70 */ 0x66, 0xFF, 0x33, + /* 113, 0x71 */ 0x66, 0xFF, 0x00, + /* 114, 0x72 */ 0x66, 0xCC, 0xFF, + /* 115, 0x73 */ 0x66, 0xCC, 0xCC, + /* 116, 0x74 */ 0x66, 0xCC, 0x99, + /* 117, 0x75 */ 0x66, 0xCC, 0x66, + /* 118, 0x76 */ 0x66, 0xCC, 0x33, + /* 119, 0x77 */ 0x66, 0xCC, 0x00, + /* 120, 0x78 */ 0x66, 0x99, 0xFF, + /* 121, 0x79 */ 0x66, 0x99, 0xCC, + /* 122, 0x7A */ 0x66, 0x99, 0x99, + /* 123, 0x7B */ 0x66, 0x99, 0x66, + /* 124, 0x7C */ 0x66, 0x99, 0x33, + /* 125, 0x7D */ 0x66, 0x99, 0x00, + /* 126, 0x7E */ 0x66, 0x66, 0xFF, + /* 127, 0x7F */ 0x66, 0x66, 0xCC, + /* 128, 0x80 */ 0x66, 0x66, 0x99, + /* 129, 0x81 */ 0x66, 0x66, 0x66, + /* 130, 0x82 */ 0x66, 0x66, 0x33, + /* 131, 0x83 */ 0x66, 0x66, 0x00, + /* 132, 0x84 */ 0x66, 0x33, 0xFF, + /* 133, 0x85 */ 0x66, 0x33, 0xCC, + /* 134, 0x86 */ 0x66, 0x33, 0x99, + /* 135, 0x87 */ 0x66, 0x33, 0x66, + /* 136, 0x88 */ 0x66, 0x33, 0x33, + /* 137, 0x89 */ 0x66, 0x33, 0x00, + /* 138, 0x8A */ 0x66, 0x00, 0xFF, + /* 139, 0x8B */ 0x66, 0x00, 0xCC, + /* 140, 0x8C */ 0x66, 0x00, 0x99, + /* 141, 0x8D */ 0x66, 0x00, 0x66, + /* 142, 0x8E */ 0x66, 0x00, 0x33, + /* 143, 0x8F */ 0x66, 0x00, 0x00, + /* 144, 0x90 */ 0x33, 0xFF, 0xFF, + /* 145, 0x91 */ 0x33, 0xFF, 0xCC, + /* 146, 0x92 */ 0x33, 0xFF, 0x99, + /* 147, 0x93 */ 0x33, 0xFF, 0x66, + /* 148, 0x94 */ 0x33, 0xFF, 0x33, + /* 149, 0x95 */ 0x33, 0xFF, 0x00, + /* 150, 0x96 */ 0x33, 0xCC, 0xFF, + /* 151, 0x97 */ 0x33, 0xCC, 0xCC, + /* 152, 0x98 */ 0x33, 0xCC, 0x99, + /* 153, 0x99 */ 0x33, 0xCC, 0x66, + /* 154, 0x9A */ 0x33, 0xCC, 0x33, + /* 155, 0x9B */ 0x33, 0xCC, 0x00, + /* 156, 0x9C */ 0x33, 0x99, 0xFF, + /* 157, 0x9D */ 0x33, 0x99, 0xCC, + /* 158, 0x9E */ 0x33, 0x99, 0x99, + /* 159, 0x9F */ 0x33, 0x99, 0x66, + /* 160, 0xA0 */ 0x33, 0x99, 0x33, + /* 161, 0xA1 */ 0x33, 0x99, 0x00, + /* 162, 0xA2 */ 0x33, 0x66, 0xFF, + /* 163, 0xA3 */ 0x33, 0x66, 0xCC, + /* 164, 0xA4 */ 0x33, 0x66, 0x99, + /* 165, 0xA5 */ 0x33, 0x66, 0x66, + /* 166, 0xA6 */ 0x33, 0x66, 0x33, + /* 167, 0xA7 */ 0x33, 0x66, 0x00, + /* 168, 0xA8 */ 0x33, 0x33, 0xFF, + /* 169, 0xA9 */ 0x33, 0x33, 0xCC, + /* 170, 0xAA */ 0x33, 0x33, 0x99, + /* 171, 0xAB */ 0x33, 0x33, 0x66, + /* 172, 0xAC */ 0x33, 0x33, 0x33, + /* 173, 0xAD */ 0x33, 0x33, 0x00, + /* 174, 0xAE */ 0x33, 0x00, 0xFF, + /* 175, 0xAF */ 0x33, 0x00, 0xCC, + /* 176, 0xB0 */ 0x33, 0x00, 0x99, + /* 177, 0xB1 */ 0x33, 0x00, 0x66, + /* 178, 0xB2 */ 0x33, 0x00, 0x33, + /* 179, 0xB3 */ 0x33, 0x00, 0x00, + /* 180, 0xB4 */ 0x00, 0xFF, 0xFF, + /* 181, 0xB5 */ 0x00, 0xFF, 0xCC, + /* 182, 0xB6 */ 0x00, 0xFF, 0x99, + /* 183, 0xB7 */ 0x00, 0xFF, 0x66, + /* 184, 0xB8 */ 0x00, 0xFF, 0x33, + /* 185, 0xB9 */ 0x00, 0xFF, 0x00, + /* 186, 0xBA */ 0x00, 0xCC, 0xFF, + /* 187, 0xBB */ 0x00, 0xCC, 0xCC, + /* 188, 0xBC */ 0x00, 0xCC, 0x99, + /* 189, 0xBD */ 0x00, 0xCC, 0x66, + /* 190, 0xBE */ 0x00, 0xCC, 0x33, + /* 191, 0xBF */ 0x00, 0xCC, 0x00, + /* 192, 0xC0 */ 0x00, 0x99, 0xFF, + /* 193, 0xC1 */ 0x00, 0x99, 0xCC, + /* 194, 0xC2 */ 0x00, 0x99, 0x99, + /* 195, 0xC3 */ 0x00, 0x99, 0x66, + /* 196, 0xC4 */ 0x00, 0x99, 0x33, + /* 197, 0xC5 */ 0x00, 0x99, 0x00, + /* 198, 0xC6 */ 0x00, 0x66, 0xFF, + /* 199, 0xC7 */ 0x00, 0x66, 0xCC, + /* 200, 0xC8 */ 0x00, 0x66, 0x99, + /* 201, 0xC9 */ 0x00, 0x66, 0x66, + /* 202, 0xCA */ 0x00, 0x66, 0x33, + /* 203, 0xCB */ 0x00, 0x66, 0x00, + /* 204, 0xCC */ 0x00, 0x33, 0xFF, + /* 205, 0xCD */ 0x00, 0x33, 0xCC, + /* 206, 0xCE */ 0x00, 0x33, 0x99, + /* 207, 0xCF */ 0x00, 0x33, 0x66, + /* 208, 0xD0 */ 0x00, 0x33, 0x33, + /* 209, 0xD1 */ 0x00, 0x33, 0x00, + /* 210, 0xD2 */ 0x00, 0x00, 0xFF, + /* 211, 0xD3 */ 0x00, 0x00, 0xCC, + /* 212, 0xD4 */ 0x00, 0x00, 0x99, + /* 213, 0xD5 */ 0x00, 0x00, 0x66, + /* 214, 0xD6 */ 0x00, 0x00, 0x33, + /* 215, 0xD7 */ 0xEE, 0x00, 0x00, + /* 216, 0xD8 */ 0xDD, 0x00, 0x00, + /* 217, 0xD9 */ 0xBB, 0x00, 0x00, + /* 218, 0xDA */ 0xAA, 0x00, 0x00, + /* 219, 0xDB */ 0x88, 0x00, 0x00, + /* 220, 0xDC */ 0x77, 0x00, 0x00, + /* 221, 0xDD */ 0x55, 0x00, 0x00, + /* 222, 0xDE */ 0x44, 0x00, 0x00, + /* 223, 0xDF */ 0x22, 0x00, 0x00, + /* 224, 0xE0 */ 0x11, 0x00, 0x00, + /* 225, 0xE1 */ 0x00, 0xEE, 0x00, + /* 226, 0xE2 */ 0x00, 0xDD, 0x00, + /* 227, 0xE3 */ 0x00, 0xBB, 0x00, + /* 228, 0xE4 */ 0x00, 0xAA, 0x00, + /* 229, 0xE5 */ 0x00, 0x88, 0x00, + /* 230, 0xE6 */ 0x00, 0x77, 0x00, + /* 231, 0xE7 */ 0x00, 0x55, 0x00, + /* 232, 0xE8 */ 0x00, 0x44, 0x00, + /* 233, 0xE9 */ 0x00, 0x22, 0x00, + /* 234, 0xEA */ 0x00, 0x11, 0x00, + /* 235, 0xEB */ 0x00, 0x00, 0xEE, + /* 236, 0xEC */ 0x00, 0x00, 0xDD, + /* 237, 0xED */ 0x00, 0x00, 0xBB, + /* 238, 0xEE */ 0x00, 0x00, 0xAA, + /* 239, 0xEF */ 0x00, 0x00, 0x88, + /* 240, 0xF0 */ 0x00, 0x00, 0x77, + /* 241, 0xF1 */ 0x00, 0x00, 0x55, + /* 242, 0xF2 */ 0x00, 0x00, 0x44, + /* 243, 0xF3 */ 0x00, 0x00, 0x22, + /* 244, 0xF4 */ 0x00, 0x00, 0x11, + /* 245, 0xF5 */ 0xEE, 0xEE, 0xEE, + /* 246, 0xF6 */ 0xDD, 0xDD, 0xDD, + /* 247, 0xF7 */ 0xBB, 0xBB, 0xBB, + /* 248, 0xF8 */ 0xAA, 0xAA, 0xAA, + /* 249, 0xF9 */ 0x88, 0x88, 0x88, + /* 250, 0xFA */ 0x77, 0x77, 0x77, + /* 251, 0xFB */ 0x55, 0x55, 0x55, + /* 252, 0xFC */ 0x44, 0x44, 0x44, + /* 253, 0xFD */ 0x22, 0x22, 0x22, + /* 254, 0xFE */ 0x11, 0x11, 0x11, + /* 255, 0xFF */ 0x00, 0x00, 0x00 +}; + +#endif /* AVFORMAT_QTPALETTE_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/r3d.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/r3d.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,410 @@ +/* + * R3D REDCODE demuxer + * Copyright (c) 2008 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +//#define DEBUG + +#include "libavutil/intreadwrite.h" +#include "libavutil/dict.h" +#include "libavutil/mathematics.h" +#include "avformat.h" +#include "internal.h" + +typedef struct { + unsigned video_offsets_count; + unsigned *video_offsets; + unsigned rdvo_offset; +} R3DContext; + +typedef struct { + unsigned size; + uint32_t tag; + uint64_t offset; +} Atom; + +static int read_atom(AVFormatContext *s, Atom *atom) +{ + atom->offset = avio_tell(s->pb); + atom->size = avio_rb32(s->pb); + if (atom->size < 8) + return -1; + atom->tag = avio_rl32(s->pb); + av_dlog(s, "atom %u %.4s offset %#"PRIx64"\n", + atom->size, (char*)&atom->tag, atom->offset); + return atom->size; +} + +static int r3d_read_red1(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + char filename[258]; + int tmp; + int av_unused tmp2; + AVRational framerate; + + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_JPEG2000; + + tmp = avio_r8(s->pb); // major version + tmp2 = avio_r8(s->pb); // minor version + av_dlog(s, "version %d.%d\n", tmp, tmp2); + + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown1 %d\n", tmp); + + tmp = avio_rb32(s->pb); + avpriv_set_pts_info(st, 32, 1, tmp); + + tmp = avio_rb32(s->pb); // filenum + av_dlog(s, "filenum %d\n", tmp); + + avio_skip(s->pb, 32); // unknown + + st->codec->width = avio_rb32(s->pb); + st->codec->height = avio_rb32(s->pb); + + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown2 %d\n", tmp); + + framerate.num = avio_rb16(s->pb); + framerate.den = avio_rb16(s->pb); + if (framerate.num && framerate.den) { +#if FF_API_R_FRAME_RATE + st->r_frame_rate = +#endif + st->avg_frame_rate = framerate; + } + + tmp = avio_r8(s->pb); // audio channels + av_dlog(s, "audio channels %d\n", tmp); + if (tmp > 0) { + AVStream *ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codec->codec_id = AV_CODEC_ID_PCM_S32BE; + ast->codec->channels = tmp; + avpriv_set_pts_info(ast, 32, 1, st->time_base.den); + } + + avio_read(s->pb, filename, 257); + filename[sizeof(filename)-1] = 0; + av_dict_set(&st->metadata, "filename", filename, 0); + + av_dlog(s, "filename %s\n", filename); + av_dlog(s, "resolution %dx%d\n", st->codec->width, st->codec->height); + av_dlog(s, "timescale %d\n", st->time_base.den); + av_dlog(s, "frame rate %d/%d\n", + framerate.num, framerate.den); + + return 0; +} + +static int r3d_read_rdvo(AVFormatContext *s, Atom *atom) +{ + R3DContext *r3d = s->priv_data; + AVStream *st = s->streams[0]; + int i; + + r3d->video_offsets_count = (atom->size - 8) / 4; + r3d->video_offsets = av_malloc(atom->size); + if (!r3d->video_offsets) + return AVERROR(ENOMEM); + + for (i = 0; i < r3d->video_offsets_count; i++) { + r3d->video_offsets[i] = avio_rb32(s->pb); + if (!r3d->video_offsets[i]) { + r3d->video_offsets_count = i; + break; + } + av_dlog(s, "video offset %d: %#x\n", i, r3d->video_offsets[i]); + } + + if (st->avg_frame_rate.num) + st->duration = av_rescale_q(r3d->video_offsets_count, + av_inv_q(st->avg_frame_rate), + st->time_base); + av_dlog(s, "duration %"PRId64"\n", st->duration); + + return 0; +} + +static void r3d_read_reos(AVFormatContext *s) +{ + R3DContext *r3d = s->priv_data; + int av_unused tmp; + + r3d->rdvo_offset = avio_rb32(s->pb); + avio_rb32(s->pb); // rdvs offset + avio_rb32(s->pb); // rdao offset + avio_rb32(s->pb); // rdas offset + + tmp = avio_rb32(s->pb); + av_dlog(s, "num video chunks %d\n", tmp); + + tmp = avio_rb32(s->pb); + av_dlog(s, "num audio chunks %d\n", tmp); + + avio_skip(s->pb, 6*4); +} + +static int r3d_read_header(AVFormatContext *s) +{ + R3DContext *r3d = s->priv_data; + Atom atom; + int ret; + + if (read_atom(s, &atom) < 0) { + av_log(s, AV_LOG_ERROR, "error reading atom\n"); + return -1; + } + if (atom.tag == MKTAG('R','E','D','1')) { + if ((ret = r3d_read_red1(s)) < 0) { + av_log(s, AV_LOG_ERROR, "error parsing 'red1' atom\n"); + return ret; + } + } else { + av_log(s, AV_LOG_ERROR, "could not find 'red1' atom\n"); + return -1; + } + + s->data_offset = avio_tell(s->pb); + av_dlog(s, "data offset %#"PRIx64"\n", s->data_offset); + if (!s->pb->seekable) + return 0; + // find REOB/REOF/REOS to load index + avio_seek(s->pb, avio_size(s->pb)-48-8, SEEK_SET); + if (read_atom(s, &atom) < 0) + av_log(s, AV_LOG_ERROR, "error reading end atom\n"); + + if (atom.tag != MKTAG('R','E','O','B') && + atom.tag != MKTAG('R','E','O','F') && + atom.tag != MKTAG('R','E','O','S')) + goto out; + + r3d_read_reos(s); + + if (r3d->rdvo_offset) { + avio_seek(s->pb, r3d->rdvo_offset, SEEK_SET); + if (read_atom(s, &atom) < 0) + av_log(s, AV_LOG_ERROR, "error reading 'rdvo' atom\n"); + if (atom.tag == MKTAG('R','D','V','O')) { + if (r3d_read_rdvo(s, &atom) < 0) + av_log(s, AV_LOG_ERROR, "error parsing 'rdvo' atom\n"); + } + } + + out: + avio_seek(s->pb, s->data_offset, SEEK_SET); + return 0; +} + +static int r3d_read_redv(AVFormatContext *s, AVPacket *pkt, Atom *atom) +{ + AVStream *st = s->streams[0]; + int tmp; + int av_unused tmp2; + uint64_t pos = avio_tell(s->pb); + unsigned dts; + int ret; + + dts = avio_rb32(s->pb); + + tmp = avio_rb32(s->pb); + av_dlog(s, "frame num %d\n", tmp); + + tmp = avio_r8(s->pb); // major version + tmp2 = avio_r8(s->pb); // minor version + av_dlog(s, "version %d.%d\n", tmp, tmp2); + + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown %d\n", tmp); + + if (tmp > 4) { + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown %d\n", tmp); + + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown %d\n", tmp); + + tmp = avio_rb32(s->pb); + av_dlog(s, "width %d\n", tmp); + tmp = avio_rb32(s->pb); + av_dlog(s, "height %d\n", tmp); + + tmp = avio_rb32(s->pb); + av_dlog(s, "metadata len %d\n", tmp); + } + tmp = atom->size - 8 - (avio_tell(s->pb) - pos); + if (tmp < 0) + return -1; + ret = av_get_packet(s->pb, pkt, tmp); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "error reading video packet\n"); + return -1; + } + + pkt->stream_index = 0; + pkt->dts = dts; + if (st->avg_frame_rate.num) + pkt->duration = (uint64_t)st->time_base.den* + st->avg_frame_rate.den/st->avg_frame_rate.num; + av_dlog(s, "pkt dts %"PRId64" duration %d\n", pkt->dts, pkt->duration); + + return 0; +} + +static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom) +{ + AVStream *st = s->streams[1]; + int av_unused tmp, tmp2; + int samples, size; + uint64_t pos = avio_tell(s->pb); + unsigned dts; + int ret; + + dts = avio_rb32(s->pb); + + st->codec->sample_rate = avio_rb32(s->pb); + if (st->codec->sample_rate < 0) { + av_log(s, AV_LOG_ERROR, "negative sample rate\n"); + return AVERROR_INVALIDDATA; + } + + samples = avio_rb32(s->pb); + + tmp = avio_rb32(s->pb); + av_dlog(s, "packet num %d\n", tmp); + + tmp = avio_rb16(s->pb); // unknown + av_dlog(s, "unknown %d\n", tmp); + + tmp = avio_r8(s->pb); // major version + tmp2 = avio_r8(s->pb); // minor version + av_dlog(s, "version %d.%d\n", tmp, tmp2); + + tmp = avio_rb32(s->pb); // unknown + av_dlog(s, "unknown %d\n", tmp); + + size = atom->size - 8 - (avio_tell(s->pb) - pos); + if (size < 0) + return -1; + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "error reading audio packet\n"); + return ret; + } + + pkt->stream_index = 1; + pkt->dts = dts; + if (st->codec->sample_rate) + pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate); + av_dlog(s, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n", + pkt->dts, pkt->duration, samples, st->codec->sample_rate); + + return 0; +} + +static int r3d_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + Atom atom; + int err = 0; + + while (!err) { + if (read_atom(s, &atom) < 0) { + err = -1; + break; + } + switch (atom.tag) { + case MKTAG('R','E','D','V'): + if (s->streams[0]->discard == AVDISCARD_ALL) + goto skip; + if (!(err = r3d_read_redv(s, pkt, &atom))) + return 0; + break; + case MKTAG('R','E','D','A'): + if (s->nb_streams < 2) + return -1; + if (s->streams[1]->discard == AVDISCARD_ALL) + goto skip; + if (!(err = r3d_read_reda(s, pkt, &atom))) + return 0; + break; + default: + skip: + avio_skip(s->pb, atom.size-8); + } + } + return err; +} + +static int r3d_probe(AVProbeData *p) +{ + if (AV_RL32(p->buf + 4) == MKTAG('R','E','D','1')) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags) +{ + AVStream *st = s->streams[0]; // video stream + R3DContext *r3d = s->priv_data; + int frame_num; + + if (!st->avg_frame_rate.num) + return -1; + + frame_num = av_rescale_q(sample_time, st->time_base, + av_inv_q(st->avg_frame_rate)); + av_dlog(s, "seek frame num %d timestamp %"PRId64"\n", + frame_num, sample_time); + + if (frame_num < r3d->video_offsets_count) { + if (avio_seek(s->pb, r3d->video_offsets_count, SEEK_SET) < 0) + return -1; + } else { + av_log(s, AV_LOG_ERROR, "could not seek to frame %d\n", frame_num); + return -1; + } + + return 0; +} + +static int r3d_close(AVFormatContext *s) +{ + R3DContext *r3d = s->priv_data; + + av_freep(&r3d->video_offsets); + + return 0; +} + +AVInputFormat ff_r3d_demuxer = { + .name = "r3d", + .long_name = NULL_IF_CONFIG_SMALL("REDCODE R3D"), + .priv_data_size = sizeof(R3DContext), + .read_probe = r3d_probe, + .read_header = r3d_read_header, + .read_packet = r3d_read_packet, + .read_close = r3d_close, + .read_seek = r3d_seek, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/r3d.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/r3d.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,20 @@ +libavformat/r3d.o libavformat/r3d.o: libavformat/r3d.c \ + libavutil/intreadwrite.h libavutil/avconfig.h libavutil/attributes.h \ + libavutil/bswap.h config.h libavutil/x86/bswap.h config.h \ + libavutil/attributes.h libavutil/x86/intreadwrite.h libavutil/dict.h \ + libavutil/mathematics.h libavutil/rational.h libavutil/intfloat.h \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/version.h \ + libavutil/intmath.h libavutil/mem.h libavutil/error.h \ + libavutil/internal.h libavutil/timer.h libavutil/x86/timer.h \ + libavutil/cpu.h libavutil/dict.h libavutil/x86/emms.h libavutil/cpu.h \ + libavutil/libm.h libavutil/mathematics.h libavutil/intfloat_readwrite.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/version.h \ + libavutil/old_pix_fmts.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/samplefmt.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/rational.h libavcodec/version.h libavcodec/old_codec_ids.h \ + libavutil/common.h libavutil/log.h libavformat/avio.h \ + libavutil/common.h libavformat/version.h libavutil/avutil.h \ + libavformat/internal.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/r3d.o Binary file ffmpeg/libavformat/r3d.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,156 @@ +/* + * RAW demuxers + * Copyright (c) 2001 Fabrice Bellard + * Copyright (c) 2005 Alex Beregszaszi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" +#include "rawdec.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/avassert.h" + +#define RAW_PACKET_SIZE 1024 + +int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size; + + size = RAW_PACKET_SIZE; + + if (av_new_packet(pkt, size) < 0) + return AVERROR(ENOMEM); + + pkt->pos= avio_tell(s->pb); + pkt->stream_index = 0; + ret = ffio_read_partial(s->pb, pkt->data, size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + av_shrink_packet(pkt, ret); + return ret; +} + +int ff_raw_audio_read_header(AVFormatContext *s) +{ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + st->start_time = 0; + /* the parameters will be extracted from the compressed bitstream */ + + return 0; +} + +/* MPEG-1/H.263 input */ +int ff_raw_video_read_header(AVFormatContext *s) +{ + AVStream *st; + FFRawVideoDemuxerContext *s1 = s->priv_data; + int ret = 0; + + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = s->iformat->raw_codec_id; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + st->codec->time_base = av_inv_q(s1->framerate); + avpriv_set_pts_info(st, 64, 1, 1200000); + +fail: + return ret; +} + +/* Note: Do not forget to add new entries to the Makefile as well. */ + +#define OFFSET(x) offsetof(FFRawVideoDemuxerContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +const AVOption ff_rawvideo_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC}, + { NULL }, +}; + +#if CONFIG_LATM_DEMUXER +AVInputFormat ff_latm_demuxer = { + .name = "latm", + .long_name = NULL_IF_CONFIG_SMALL("raw LOAS/LATM"), + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "latm", + .raw_codec_id = AV_CODEC_ID_AAC_LATM, +}; +#endif + +#if CONFIG_MJPEG_DEMUXER +FF_DEF_RAWVIDEO_DEMUXER(mjpeg, "raw MJPEG video", NULL, "mjpg,mjpeg,mpo", AV_CODEC_ID_MJPEG) +#endif + +#if CONFIG_MLP_DEMUXER +AVInputFormat ff_mlp_demuxer = { + .name = "mlp", + .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "mlp", + .raw_codec_id = AV_CODEC_ID_MLP, +}; +#endif + +#if CONFIG_TRUEHD_DEMUXER +AVInputFormat ff_truehd_demuxer = { + .name = "truehd", + .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "thd", + .raw_codec_id = AV_CODEC_ID_TRUEHD, +}; +#endif + +#if CONFIG_SHORTEN_DEMUXER +AVInputFormat ff_shorten_demuxer = { + .name = "shn", + .long_name = NULL_IF_CONFIG_SMALL("raw Shorten"), + .read_header = ff_raw_audio_read_header, + .read_packet = ff_raw_read_partial_packet, + .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, + .extensions = "shn", + .raw_codec_id = AV_CODEC_ID_SHORTEN, +}; +#endif + +#if CONFIG_VC1_DEMUXER +FF_DEF_RAWVIDEO_DEMUXER(vc1, "raw VC-1", NULL, "vc1", AV_CODEC_ID_VC1) +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawdec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawdec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,21 @@ +libavformat/rawdec.o libavformat/rawdec.o: libavformat/rawdec.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/internal.h \ + libavformat/avio_internal.h libavformat/url.h libavformat/rawdec.h \ + libavutil/opt.h libavutil/parseutils.h libavutil/pixdesc.h \ + libavutil/avassert.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawdec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawdec.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,67 @@ +/* + * RAW demuxers + * Copyright (C) 2007 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_RAWDEC_H +#define AVFORMAT_RAWDEC_H + +#include "avformat.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +typedef struct FFRawVideoDemuxerContext { + const AVClass *class; /**< Class for private options. */ + char *video_size; /**< String describing video size, set by a private option. */ + char *pixel_format; /**< Set by a private option. */ + AVRational framerate; /**< AVRational describing framerate, set by a private option. */ +} FFRawVideoDemuxerContext; + +extern const AVOption ff_rawvideo_options[]; + +int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt); + +int ff_raw_audio_read_header(AVFormatContext *s); + +int ff_raw_video_read_header(AVFormatContext *s); + +#define FF_RAWVIDEO_DEMUXER_CLASS(name)\ +static const AVClass name ## _demuxer_class = {\ + .class_name = #name " demuxer",\ + .item_name = av_default_item_name,\ + .option = ff_rawvideo_options,\ + .version = LIBAVUTIL_VERSION_INT,\ +}; + +#define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\ +FF_RAWVIDEO_DEMUXER_CLASS(shortname)\ +AVInputFormat ff_ ## shortname ## _demuxer = {\ + .name = #shortname,\ + .long_name = NULL_IF_CONFIG_SMALL(longname),\ + .read_probe = probe,\ + .read_header = ff_raw_video_read_header,\ + .read_packet = ff_raw_read_partial_packet,\ + .extensions = ext,\ + .flags = AVFMT_GENERIC_INDEX,\ + .raw_codec_id = id,\ + .priv_data_size = sizeof(FFRawVideoDemuxerContext),\ + .priv_class = &shortname ## _demuxer_class,\ +}; + +#endif /* AVFORMAT_RAWDEC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawdec.o Binary file ffmpeg/libavformat/rawdec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawenc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawenc.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,281 @@ +/* + * RAW muxers + * Copyright (c) 2001 Fabrice Bellard + * Copyright (c) 2005 Alex Beregszaszi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rawenc.h" + +int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} + +/* Note: Do not forget to add new entries to the Makefile as well. */ + +#if CONFIG_AC3_MUXER +AVOutputFormat ff_ac3_muxer = { + .name = "ac3", + .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), + .mime_type = "audio/x-ac3", + .extensions = "ac3", + .audio_codec = AV_CODEC_ID_AC3, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_ADX_MUXER +AVOutputFormat ff_adx_muxer = { + .name = "adx", + .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), + .extensions = "adx", + .audio_codec = AV_CODEC_ID_ADPCM_ADX, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_CAVSVIDEO_MUXER +AVOutputFormat ff_cavsvideo_muxer = { + .name = "cavsvideo", + .long_name = NULL_IF_CONFIG_SMALL("raw Chinese AVS (Audio Video Standard) video"), + .extensions = "cavs", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_CAVS, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_DIRAC_MUXER +AVOutputFormat ff_dirac_muxer = { + .name = "dirac", + .long_name = NULL_IF_CONFIG_SMALL("raw Dirac"), + .extensions = "drc", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_DIRAC, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_DNXHD_MUXER +AVOutputFormat ff_dnxhd_muxer = { + .name = "dnxhd", + .long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"), + .extensions = "dnxhd", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_DNXHD, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_DTS_MUXER +AVOutputFormat ff_dts_muxer = { + .name = "dts", + .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), + .mime_type = "audio/x-dca", + .extensions = "dts", + .audio_codec = AV_CODEC_ID_DTS, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_EAC3_MUXER +AVOutputFormat ff_eac3_muxer = { + .name = "eac3", + .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), + .mime_type = "audio/x-eac3", + .extensions = "eac3", + .audio_codec = AV_CODEC_ID_EAC3, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_G722_MUXER +AVOutputFormat ff_g722_muxer = { + .name = "g722", + .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), + .mime_type = "audio/G722", + .extensions = "g722", + .audio_codec = AV_CODEC_ID_ADPCM_G722, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_G723_1_MUXER +AVOutputFormat ff_g723_1_muxer = { + .name = "g723_1", + .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), + .mime_type = "audio/g723", + .extensions = "tco,rco", + .audio_codec = AV_CODEC_ID_G723_1, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_H261_MUXER +AVOutputFormat ff_h261_muxer = { + .name = "h261", + .long_name = NULL_IF_CONFIG_SMALL("raw H.261"), + .mime_type = "video/x-h261", + .extensions = "h261", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_H261, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_H263_MUXER +AVOutputFormat ff_h263_muxer = { + .name = "h263", + .long_name = NULL_IF_CONFIG_SMALL("raw H.263"), + .mime_type = "video/x-h263", + .extensions = "h263", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_H263, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_H264_MUXER +AVOutputFormat ff_h264_muxer = { + .name = "h264", + .long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"), + .extensions = "h264", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_H264, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_M4V_MUXER +AVOutputFormat ff_m4v_muxer = { + .name = "m4v", + .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-4 video"), + .extensions = "m4v", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_MPEG4, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_MJPEG_MUXER +AVOutputFormat ff_mjpeg_muxer = { + .name = "mjpeg", + .long_name = NULL_IF_CONFIG_SMALL("raw MJPEG video"), + .mime_type = "video/x-mjpeg", + .extensions = "mjpg,mjpeg", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_MJPEG, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_MLP_MUXER +AVOutputFormat ff_mlp_muxer = { + .name = "mlp", + .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), + .extensions = "mlp", + .audio_codec = AV_CODEC_ID_MLP, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_MPEG1VIDEO_MUXER +AVOutputFormat ff_mpeg1video_muxer = { + .name = "mpeg1video", + .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), + .mime_type = "video/x-mpeg", + .extensions = "mpg,mpeg,m1v", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_MPEG1VIDEO, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_MPEG2VIDEO_MUXER +AVOutputFormat ff_mpeg2video_muxer = { + .name = "mpeg2video", + .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-2 video"), + .extensions = "m2v", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_MPEG2VIDEO, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_RAWVIDEO_MUXER +AVOutputFormat ff_rawvideo_muxer = { + .name = "rawvideo", + .long_name = NULL_IF_CONFIG_SMALL("raw video"), + .extensions = "yuv,rgb", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_TRUEHD_MUXER +AVOutputFormat ff_truehd_muxer = { + .name = "truehd", + .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), + .extensions = "thd", + .audio_codec = AV_CODEC_ID_TRUEHD, + .video_codec = AV_CODEC_ID_NONE, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + +#if CONFIG_VC1_MUXER +AVOutputFormat ff_vc1_muxer = { + .name = "vc1", + .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), + .extensions = "vc1", + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_VC1, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawenc.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawenc.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,18 @@ +libavformat/rawenc.o libavformat/rawenc.o: libavformat/rawenc.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavformat/rawenc.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawenc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawenc.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,29 @@ +/* + * RAW muxers + * Copyright (C) 2007 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_RAWENC_H +#define AVFORMAT_RAWENC_H + +#include "avformat.h" + +int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt); + +#endif /* AVFORMAT_RAWENC_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawenc.o Binary file ffmpeg/libavformat/rawenc.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawvideodec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawvideodec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,115 @@ +/* + * RAW video demuxer + * Copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "avformat.h" + +typedef struct RawVideoDemuxerContext { + const AVClass *class; /**< Class for private options. */ + int width, height; /**< Integers describing video size, set by a private option. */ + char *pixel_format; /**< Set by a private option. */ + AVRational framerate; /**< AVRational describing framerate, set by a private option. */ +} RawVideoDemuxerContext; + + +static int rawvideo_read_header(AVFormatContext *ctx) +{ + RawVideoDemuxerContext *s = ctx->priv_data; + enum AVPixelFormat pix_fmt; + AVStream *st; + + st = avformat_new_stream(ctx, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + + st->codec->codec_id = ctx->iformat->raw_codec_id; + + if ((pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { + av_log(ctx, AV_LOG_ERROR, "No such pixel format: %s.\n", + s->pixel_format); + return AVERROR(EINVAL); + } + + avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num); + + st->codec->width = s->width; + st->codec->height = s->height; + st->codec->pix_fmt = pix_fmt; + st->codec->bit_rate = av_rescale_q(avpicture_get_size(st->codec->pix_fmt, s->width, s->height), + (AVRational){8,1}, st->time_base); + + return 0; +} + + +static int rawvideo_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int packet_size, ret, width, height; + AVStream *st = s->streams[0]; + + width = st->codec->width; + height = st->codec->height; + + packet_size = avpicture_get_size(st->codec->pix_fmt, width, height); + if (packet_size < 0) + return -1; + + ret = av_get_packet(s->pb, pkt, packet_size); + pkt->pts = pkt->dts = pkt->pos / packet_size; + + pkt->stream_index = 0; + if (ret < 0) + return ret; + return 0; +} + +#define OFFSET(x) offsetof(RawVideoDemuxerContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption rawvideo_options[] = { + { "video_size", "set frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, + { "pixel_format", "set pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = "yuv420p"}, 0, 0, DEC }, + { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, + { NULL }, +}; + +static const AVClass rawvideo_demuxer_class = { + .class_name = "rawvideo demuxer", + .item_name = av_default_item_name, + .option = rawvideo_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_rawvideo_demuxer = { + .name = "rawvideo", + .long_name = NULL_IF_CONFIG_SMALL("raw video"), + .priv_data_size = sizeof(RawVideoDemuxerContext), + .read_header = rawvideo_read_header, + .read_packet = rawvideo_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .extensions = "yuv,cif,qcif,rgb", + .raw_codec_id = AV_CODEC_ID_RAWVIDEO, + .priv_class = &rawvideo_demuxer_class, +}; diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawvideodec.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rawvideodec.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,19 @@ +libavformat/rawvideodec.o libavformat/rawvideodec.o: \ + libavformat/rawvideodec.c libavutil/parseutils.h libavutil/rational.h \ + libavutil/attributes.h libavutil/pixdesc.h libavutil/pixfmt.h \ + libavutil/avconfig.h libavutil/version.h libavutil/old_pix_fmts.h \ + libavutil/opt.h libavutil/avutil.h libavutil/common.h \ + libavutil/version.h config.h libavutil/intmath.h libavutil/mem.h \ + libavutil/error.h libavutil/internal.h libavutil/timer.h \ + libavutil/x86/timer.h libavutil/cpu.h libavutil/dict.h \ + libavutil/x86/emms.h config.h libavutil/attributes.h libavutil/cpu.h \ + libavutil/libm.h libavutil/intfloat.h libavutil/mathematics.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/samplefmt.h \ + libavformat/internal.h libavformat/avformat.h libavcodec/avcodec.h \ + libavutil/samplefmt.h libavutil/avutil.h libavutil/buffer.h \ + libavutil/cpu.h libavutil/channel_layout.h libavutil/dict.h \ + libavutil/frame.h libavcodec/version.h libavutil/buffer.h \ + libavutil/log.h libavutil/pixfmt.h libavutil/rational.h \ + libavcodec/version.h libavcodec/old_codec_ids.h libavutil/common.h \ + libavutil/dict.h libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rawvideodec.o Binary file ffmpeg/libavformat/rawvideodec.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rdt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rdt.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,572 @@ +/* + * Realmedia RTSP protocol (RDT) support. + * Copyright (c) 2007 Ronald S. Bultje + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @brief Realmedia RTSP protocol (RDT) support + * @author Ronald S. Bultje + */ + +#include "avformat.h" +#include "libavutil/avstring.h" +#include "rtpdec.h" +#include "rdt.h" +#include "libavutil/base64.h" +#include "libavutil/md5.h" +#include "rm.h" +#include "internal.h" +#include "avio_internal.h" +#include "libavcodec/get_bits.h" + +struct RDTDemuxContext { + AVFormatContext *ic; /**< the containing (RTSP) demux context */ + /** Each RDT stream-set (represented by one RTSPStream) can contain + * multiple streams (of the same content, but with possibly different + * codecs/bitrates). Each such stream is represented by one AVStream + * in the AVFormatContext, and this variable points to the offset in + * that array such that the first is the first stream of this set. */ + AVStream **streams; + int n_streams; /**< streams with identifical content in this set */ + void *dynamic_protocol_context; + DynamicPayloadPacketHandlerProc parse_packet; + uint32_t prev_timestamp; + int prev_set_id, prev_stream_id; +}; + +RDTDemuxContext * +ff_rdt_parse_open(AVFormatContext *ic, int first_stream_of_set_idx, + void *priv_data, RTPDynamicProtocolHandler *handler) +{ + RDTDemuxContext *s = av_mallocz(sizeof(RDTDemuxContext)); + if (!s) + return NULL; + + s->ic = ic; + s->streams = &ic->streams[first_stream_of_set_idx]; + do { + s->n_streams++; + } while (first_stream_of_set_idx + s->n_streams < ic->nb_streams && + s->streams[s->n_streams]->id == s->streams[0]->id); + s->prev_set_id = -1; + s->prev_stream_id = -1; + s->prev_timestamp = -1; + s->parse_packet = handler ? handler->parse_packet : NULL; + s->dynamic_protocol_context = priv_data; + + return s; +} + +void +ff_rdt_parse_close(RDTDemuxContext *s) +{ + av_free(s); +} + +struct PayloadContext { + AVFormatContext *rmctx; + int nb_rmst; + RMStream **rmst; + uint8_t *mlti_data; + unsigned int mlti_data_size; + char buffer[RTP_MAX_PACKET_LENGTH + FF_INPUT_BUFFER_PADDING_SIZE]; + int audio_pkt_cnt; /**< remaining audio packets in rmdec */ +}; + +void +ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], + const char *challenge) +{ + int ch_len = strlen (challenge), i; + unsigned char zres[16], + buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 }; +#define XOR_TABLE_SIZE 37 + const unsigned char xor_table[XOR_TABLE_SIZE] = { + 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53, + 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70, + 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09, + 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02, + 0x10, 0x57, 0x05, 0x18, 0x54 }; + + /* some (length) checks */ + if (ch_len == 40) /* what a hack... */ + ch_len = 32; + else if (ch_len > 56) + ch_len = 56; + memcpy(buf + 8, challenge, ch_len); + + /* xor challenge bytewise with xor_table */ + for (i = 0; i < XOR_TABLE_SIZE; i++) + buf[8 + i] ^= xor_table[i]; + + av_md5_sum(zres, buf, 64); + ff_data_to_hex(response, zres, 16, 1); + + /* add tail */ + strcpy (response + 32, "01d0a8e3"); + + /* calculate checksum */ + for (i = 0; i < 8; i++) + chksum[i] = response[i * 4]; + chksum[8] = 0; +} + +static int +rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) +{ + AVIOContext pb; + int size; + uint32_t tag; + + /** + * Layout of the MLTI chunk: + * 4: MLTI + * 2: number of streams + * Then for each stream ([number_of_streams] times): + * 2: mdpr index + * 2: number of mdpr chunks + * Then for each mdpr chunk ([number_of_mdpr_chunks] times): + * 4: size + * [size]: data + * we skip MDPR chunks until we reach the one of the stream + * we're interested in, and forward that ([size]+[data]) to + * the RM demuxer to parse the stream-specific header data. + */ + if (!rdt->mlti_data) + return -1; + ffio_init_context(&pb, rdt->mlti_data, rdt->mlti_data_size, 0, + NULL, NULL, NULL, NULL); + tag = avio_rl32(&pb); + if (tag == MKTAG('M', 'L', 'T', 'I')) { + int num, chunk_nr; + + /* read index of MDPR chunk numbers */ + num = avio_rb16(&pb); + if (rule_nr < 0 || rule_nr >= num) + return -1; + avio_skip(&pb, rule_nr * 2); + chunk_nr = avio_rb16(&pb); + avio_skip(&pb, (num - 1 - rule_nr) * 2); + + /* read MDPR chunks */ + num = avio_rb16(&pb); + if (chunk_nr >= num) + return -1; + while (chunk_nr--) + avio_skip(&pb, avio_rb32(&pb)); + size = avio_rb32(&pb); + } else { + size = rdt->mlti_data_size; + avio_seek(&pb, 0, SEEK_SET); + } + if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, rdt->rmst[st->index], size, NULL) < 0) + return -1; + + return 0; +} + +/** + * Actual data handling. + */ + +int +ff_rdt_parse_header(const uint8_t *buf, int len, + int *pset_id, int *pseq_no, int *pstream_id, + int *pis_keyframe, uint32_t *ptimestamp) +{ + GetBitContext gb; + int consumed = 0, set_id, seq_no, stream_id, is_keyframe, + len_included, need_reliable; + uint32_t timestamp; + + /* skip status packets */ + while (len >= 5 && buf[1] == 0xFF /* status packet */) { + int pkt_len; + + if (!(buf[0] & 0x80)) + return -1; /* not followed by a data packet */ + + pkt_len = AV_RB16(buf+3); + buf += pkt_len; + len -= pkt_len; + consumed += pkt_len; + } + if (len < 16) + return -1; + /** + * Layout of the header (in bits): + * 1: len_included + * Flag indicating whether this header includes a length field; + * this can be used to concatenate multiple RDT packets in a + * single UDP/TCP data frame and is used to precede RDT data + * by stream status packets + * 1: need_reliable + * Flag indicating whether this header includes a "reliable + * sequence number"; these are apparently sequence numbers of + * data packets alone. For data packets, this flag is always + * set, according to the Real documentation [1] + * 5: set_id + * ID of a set of streams of identical content, possibly with + * different codecs or bitrates + * 1: is_reliable + * Flag set for certain streams deemed less tolerable for packet + * loss + * 16: seq_no + * Packet sequence number; if >=0xFF00, this is a non-data packet + * containing stream status info, the second byte indicates the + * type of status packet (see wireshark docs / source code [2]) + * if (len_included) { + * 16: packet_len + * } else { + * packet_len = remainder of UDP/TCP frame + * } + * 1: is_back_to_back + * Back-to-Back flag; used for timing, set for one in every 10 + * packets, according to the Real documentation [1] + * 1: is_slow_data + * Slow-data flag; currently unused, according to Real docs [1] + * 5: stream_id + * ID of the stream within this particular set of streams + * 1: is_no_keyframe + * Non-keyframe flag (unset if packet belongs to a keyframe) + * 32: timestamp (PTS) + * if (set_id == 0x1F) { + * 16: set_id (extended set-of-streams ID; see set_id) + * } + * if (need_reliable) { + * 16: reliable_seq_no + * Reliable sequence number (see need_reliable) + * } + * if (stream_id == 0x3F) { + * 16: stream_id (extended stream ID; see stream_id) + * } + * [1] https://protocol.helixcommunity.org/files/2005/devdocs/RDT_Feature_Level_20.txt + * [2] http://www.wireshark.org/docs/dfref/r/rdt.html and + * http://anonsvn.wireshark.org/viewvc/trunk/epan/dissectors/packet-rdt.c + */ + init_get_bits(&gb, buf, len << 3); + len_included = get_bits1(&gb); + need_reliable = get_bits1(&gb); + set_id = get_bits(&gb, 5); + skip_bits(&gb, 1); + seq_no = get_bits(&gb, 16); + if (len_included) + skip_bits(&gb, 16); + skip_bits(&gb, 2); + stream_id = get_bits(&gb, 5); + is_keyframe = !get_bits1(&gb); + timestamp = get_bits_long(&gb, 32); + if (set_id == 0x1f) + set_id = get_bits(&gb, 16); + if (need_reliable) + skip_bits(&gb, 16); + if (stream_id == 0x1f) + stream_id = get_bits(&gb, 16); + + if (pset_id) *pset_id = set_id; + if (pseq_no) *pseq_no = seq_no; + if (pstream_id) *pstream_id = stream_id; + if (pis_keyframe) *pis_keyframe = is_keyframe; + if (ptimestamp) *ptimestamp = timestamp; + + return consumed + (get_bits_count(&gb) >> 3); +} + +/**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */ +static int +rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st, + AVPacket *pkt, uint32_t *timestamp, + const uint8_t *buf, int len, uint16_t rtp_seq, int flags) +{ + int seq = 1, res; + AVIOContext pb; + + if (rdt->audio_pkt_cnt == 0) { + int pos; + + ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL); + flags = (flags & RTP_FLAG_KEY) ? 2 : 0; + res = ff_rm_parse_packet (rdt->rmctx, &pb, st, rdt->rmst[st->index], len, pkt, + &seq, flags, *timestamp); + pos = avio_tell(&pb); + if (res < 0) + return res; + if (res > 0) { + if (st->codec->codec_id == AV_CODEC_ID_AAC) { + memcpy (rdt->buffer, buf + pos, len - pos); + rdt->rmctx->pb = avio_alloc_context (rdt->buffer, len - pos, 0, + NULL, NULL, NULL, NULL); + } + goto get_cache; + } + } else { +get_cache: + rdt->audio_pkt_cnt = + ff_rm_retrieve_cache (rdt->rmctx, rdt->rmctx->pb, + st, rdt->rmst[st->index], pkt); + if (rdt->audio_pkt_cnt == 0 && + st->codec->codec_id == AV_CODEC_ID_AAC) + av_freep(&rdt->rmctx->pb); + } + pkt->stream_index = st->index; + pkt->pts = *timestamp; + + return rdt->audio_pkt_cnt > 0; +} + +int +ff_rdt_parse_packet(RDTDemuxContext *s, AVPacket *pkt, + uint8_t **bufptr, int len) +{ + uint8_t *buf = bufptr ? *bufptr : NULL; + int seq_no, flags = 0, stream_id, set_id, is_keyframe; + uint32_t timestamp; + int rv= 0; + + if (!s->parse_packet) + return -1; + + if (!buf && s->prev_stream_id != -1) { + /* return the next packets, if any */ + timestamp= 0; ///< Should not be used if buf is NULL, but should be set to the timestamp of the packet returned.... + rv= s->parse_packet(s->ic, s->dynamic_protocol_context, + s->streams[s->prev_stream_id], + pkt, ×tamp, NULL, 0, 0, flags); + return rv; + } + + if (len < 12) + return -1; + rv = ff_rdt_parse_header(buf, len, &set_id, &seq_no, &stream_id, &is_keyframe, ×tamp); + if (rv < 0) + return rv; + if (is_keyframe && + (set_id != s->prev_set_id || timestamp != s->prev_timestamp || + stream_id != s->prev_stream_id)) { + flags |= RTP_FLAG_KEY; + s->prev_set_id = set_id; + s->prev_timestamp = timestamp; + } + s->prev_stream_id = stream_id; + buf += rv; + len -= rv; + + if (s->prev_stream_id >= s->n_streams) { + s->prev_stream_id = -1; + return -1; + } + + rv = s->parse_packet(s->ic, s->dynamic_protocol_context, + s->streams[s->prev_stream_id], + pkt, ×tamp, buf, len, 0, flags); + + return rv; +} + +void +ff_rdt_subscribe_rule (char *cmd, int size, + int stream_nr, int rule_nr) +{ + av_strlcatf(cmd, size, "stream=%d;rule=%d,stream=%d;rule=%d", + stream_nr, rule_nr * 2, stream_nr, rule_nr * 2 + 1); +} + +static unsigned char * +rdt_parse_b64buf (unsigned int *target_len, const char *p) +{ + unsigned char *target; + int len = strlen(p); + if (*p == '\"') { + p++; + len -= 2; /* skip embracing " at start/end */ + } + *target_len = len * 3 / 4; + target = av_mallocz(*target_len + FF_INPUT_BUFFER_PADDING_SIZE); + av_base64_decode(target, p, *target_len); + return target; +} + +static int +rdt_parse_sdp_line (AVFormatContext *s, int st_index, + PayloadContext *rdt, const char *line) +{ + AVStream *stream = s->streams[st_index]; + const char *p = line; + + if (av_strstart(p, "OpaqueData:buffer;", &p)) { + rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p); + } else if (av_strstart(p, "StartTime:integer;", &p)) + stream->first_dts = atoi(p); + else if (av_strstart(p, "ASMRuleBook:string;", &p)) { + int n, first = -1; + + for (n = 0; n < s->nb_streams; n++) + if (s->streams[n]->id == stream->id) { + int count = s->streams[n]->index + 1; + if (first == -1) first = n; + if (rdt->nb_rmst < count) { + RMStream **rmst= av_realloc(rdt->rmst, count*sizeof(*rmst)); + if (!rmst) + return AVERROR(ENOMEM); + memset(rmst + rdt->nb_rmst, 0, + (count - rdt->nb_rmst) * sizeof(*rmst)); + rdt->rmst = rmst; + rdt->nb_rmst = count; + } + rdt->rmst[s->streams[n]->index] = ff_rm_alloc_rmstream(); + rdt_load_mdpr(rdt, s->streams[n], (n - first) * 2); + } + } + + return 0; +} + +static void +real_parse_asm_rule(AVStream *st, const char *p, const char *end) +{ + do { + /* can be either averagebandwidth= or AverageBandwidth= */ + if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%d", &st->codec->bit_rate) == 1) + break; + if (!(p = strchr(p, ',')) || p > end) + p = end; + p++; + } while (p < end); +} + +static AVStream * +add_dstream(AVFormatContext *s, AVStream *orig_st) +{ + AVStream *st; + + if (!(st = avformat_new_stream(s, NULL))) + return NULL; + st->id = orig_st->id; + st->codec->codec_type = orig_st->codec->codec_type; + st->first_dts = orig_st->first_dts; + + return st; +} + +static void +real_parse_asm_rulebook(AVFormatContext *s, AVStream *orig_st, + const char *p) +{ + const char *end; + int n_rules = 0, odd = 0; + AVStream *st; + + /** + * The ASMRuleBook contains a list of comma-separated strings per rule, + * and each rule is separated by a ;. The last one also has a ; at the + * end so we can use it as delimiter. + * Every rule occurs twice, once for when the RTSP packet header marker + * is set and once for if it isn't. We only read the first because we + * don't care much (that's what the "odd" variable is for). + * Each rule contains a set of one or more statements, optionally + * preceded by a single condition. If there's a condition, the rule + * starts with a '#'. Multiple conditions are merged between brackets, + * so there are never multiple conditions spread out over separate + * statements. Generally, these conditions are bitrate limits (min/max) + * for multi-bitrate streams. + */ + if (*p == '\"') p++; + while (1) { + if (!(end = strchr(p, ';'))) + break; + if (!odd && end != p) { + if (n_rules > 0) + st = add_dstream(s, orig_st); + else + st = orig_st; + if (!st) + break; + real_parse_asm_rule(st, p, end); + n_rules++; + } + p = end + 1; + odd ^= 1; + } +} + +void +ff_real_parse_sdp_a_line (AVFormatContext *s, int stream_index, + const char *line) +{ + const char *p = line; + + if (av_strstart(p, "ASMRuleBook:string;", &p)) + real_parse_asm_rulebook(s, s->streams[stream_index], p); +} + +static PayloadContext * +rdt_new_context (void) +{ + PayloadContext *rdt = av_mallocz(sizeof(PayloadContext)); + + int ret = avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer, NULL); + if (ret < 0) { + av_free(rdt); + return NULL; + } + + return rdt; +} + +static void +rdt_free_context (PayloadContext *rdt) +{ + int i; + + for (i = 0; i < rdt->nb_rmst; i++) + if (rdt->rmst[i]) { + ff_rm_free_rmstream(rdt->rmst[i]); + av_freep(&rdt->rmst[i]); + } + if (rdt->rmctx) + avformat_close_input(&rdt->rmctx); + av_freep(&rdt->mlti_data); + av_freep(&rdt->rmst); + av_free(rdt); +} + +#define RDT_HANDLER(n, s, t) \ +static RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \ + .enc_name = s, \ + .codec_type = t, \ + .codec_id = AV_CODEC_ID_NONE, \ + .parse_sdp_a_line = rdt_parse_sdp_line, \ + .alloc = rdt_new_context, \ + .free = rdt_free_context, \ + .parse_packet = rdt_parse_packet \ +} + +RDT_HANDLER(live_video, "x-pn-multirate-realvideo-live", AVMEDIA_TYPE_VIDEO); +RDT_HANDLER(live_audio, "x-pn-multirate-realaudio-live", AVMEDIA_TYPE_AUDIO); +RDT_HANDLER(video, "x-pn-realvideo", AVMEDIA_TYPE_VIDEO); +RDT_HANDLER(audio, "x-pn-realaudio", AVMEDIA_TYPE_AUDIO); + +void av_register_rdt_dynamic_payload_handlers(void) +{ + ff_register_dynamic_payload_handler(&ff_rdt_video_handler); + ff_register_dynamic_payload_handler(&ff_rdt_audio_handler); + ff_register_dynamic_payload_handler(&ff_rdt_live_video_handler); + ff_register_dynamic_payload_handler(&ff_rdt_live_audio_handler); +} diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rdt.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rdt.d Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,26 @@ +libavformat/rdt.o libavformat/rdt.o: libavformat/rdt.c \ + libavformat/avformat.h libavcodec/avcodec.h libavutil/samplefmt.h \ + libavutil/avutil.h libavutil/common.h libavutil/attributes.h \ + libavutil/version.h libavutil/avconfig.h config.h libavutil/intmath.h \ + libavutil/mem.h libavutil/error.h libavutil/internal.h \ + libavutil/timer.h libavutil/x86/timer.h libavutil/cpu.h \ + libavutil/dict.h libavutil/x86/emms.h config.h libavutil/attributes.h \ + libavutil/cpu.h libavutil/libm.h libavutil/intfloat.h \ + libavutil/mathematics.h libavutil/rational.h \ + libavutil/intfloat_readwrite.h libavutil/log.h libavutil/pixfmt.h \ + libavutil/version.h libavutil/old_pix_fmts.h libavutil/avutil.h \ + libavutil/buffer.h libavutil/cpu.h libavutil/channel_layout.h \ + libavutil/dict.h libavutil/frame.h libavcodec/version.h \ + libavutil/buffer.h libavutil/samplefmt.h libavutil/log.h \ + libavutil/pixfmt.h libavutil/rational.h libavcodec/version.h \ + libavcodec/old_codec_ids.h libavutil/common.h libavutil/dict.h \ + libavutil/log.h libavformat/avio.h libavutil/common.h \ + libavformat/version.h libavutil/avutil.h libavutil/avstring.h \ + libavformat/rtpdec.h libavformat/rtp.h libavformat/avformat.h \ + libavformat/url.h libavformat/srtp.h libavformat/rdt.h \ + libavutil/base64.h libavutil/md5.h libavformat/rm.h \ + libavformat/internal.h libavformat/avio_internal.h \ + libavcodec/get_bits.h libavutil/intreadwrite.h libavutil/bswap.h \ + libavutil/x86/bswap.h libavutil/x86/intreadwrite.h libavutil/avassert.h \ + libavcodec/mathops.h config.h libavcodec/x86/mathops.h config.h \ + libavutil/common.h diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rdt.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/rdt.h Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,112 @@ +/* + * Realmedia RTSP (RDT) definitions + * Copyright (c) 2007 Ronald S. Bultje + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_RDT_H +#define AVFORMAT_RDT_H + +#include +#include "avformat.h" +#include "rtpdec.h" + +typedef struct RDTDemuxContext RDTDemuxContext; + +/** + * Allocate and init the RDT parsing context. + * @param ic the containing RTSP demuxer context + * @param first_stream_of_set_idx index to the first AVStream in the RTSP + * demuxer context's ic->streams array that is part of this + * particular stream's set of streams (with identical content) + * @param priv_data private data of the payload data handler context + * @param handler pointer to the parse_packet() payload parsing function + * @return a newly allocated RDTDemuxContext. Free with ff_rdt_parse_close(). + */ +RDTDemuxContext *ff_rdt_parse_open(AVFormatContext *ic, + int first_stream_of_set_idx, + void *priv_data, + RTPDynamicProtocolHandler *handler); +void ff_rdt_parse_close(RDTDemuxContext *s); + +/** + * Calculate the response (RealChallenge2 in the RTSP header) to the + * challenge (RealChallenge1 in the RTSP header from the Real/Helix + * server), which is used as some sort of client validation. + * + * @param response pointer to response buffer, it should be at least 41 bytes + * (40 data + 1 zero) bytes long. + * @param chksum pointer to buffer containing a checksum of the response, + * it should be at least 9 (8 data + 1 zero) bytes long. + * @param challenge pointer to the RealChallenge1 value provided by the + * server. + */ +void ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], + const char *challenge); + +/** + * Register RDT-related dynamic payload handlers with our cache. + */ +void av_register_rdt_dynamic_payload_handlers(void); + +/** + * Add subscription information to Subscribe parameter string. + * + * @param cmd string to write the subscription information into. + * @param size size of cmd. + * @param stream_nr stream number. + * @param rule_nr rule number to conform to. + */ +void ff_rdt_subscribe_rule(char *cmd, int size, + int stream_nr, int rule_nr); + +/** + * Parse RDT-style packet header. + * + * @param buf input buffer + * @param len length of input buffer + * @param pset_id will be set to the set ID this packet belongs to + * @param pseq_no will be set to the sequence number of the packet + * @param pstream_id will be set to the stream ID this packet belongs to + * @param pis_keyframe will be whether this packet belongs to a keyframe + * @param ptimestamp will be set to the timestamp of the packet + * @return the amount of bytes consumed, or negative on error + */ +int ff_rdt_parse_header(const uint8_t *buf, int len, + int *pset_id, int *pseq_no, int *pstream_id, + int *pis_keyframe, uint32_t *ptimestamp); + +/** + * Parse RDT-style packet data (header + media data). + * Usage similar to rtp_parse_packet(). + */ +int ff_rdt_parse_packet(RDTDemuxContext *s, AVPacket *pkt, + uint8_t **buf, int len); + +/** + * Parse a server-related SDP line. + * + * @param s the RTSP AVFormatContext + * @param stream_index the index of the first stream in the set represented + * by the SDP m= line (in s->streams) + * @param buf the SDP line + */ +void ff_real_parse_sdp_a_line(AVFormatContext *s, int stream_index, + const char *buf); + +#endif /* AVFORMAT_RDT_H */ diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/rdt.o Binary file ffmpeg/libavformat/rdt.o has changed diff -r 6840f77b83aa -r f445c3017523 ffmpeg/libavformat/realtextdec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffmpeg/libavformat/realtextdec.c Sun Apr 21 11:16:23 2013 +0200 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * RealText subtitle demuxer + * @see http://service.real.com/help/library/guides/ProductionGuide/prodguide/htmfiles/realtext.htm + */ + +#include "avformat.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" + +typedef struct { + FFDemuxSubtitlesQueue q; +} RealTextContext; + +static int realtext_probe(AVProbeData *p) +{ + const unsigned char *ptr = p->buf; + + if (AV_RB24(ptr) == 0xEFBBBF) + ptr += 3; /* skip UTF-8 BOM */ + return !av_strncasecmp(ptr, "priv_data; + AVStream *st = avformat_new_stream(s, NULL); + AVBPrint buf; + char c = 0; + int res = 0, duration = read_ts("60"); // default duration is 60 seconds + + if (!st) + return AVERROR(ENOMEM); + avpriv_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->codec_id = AV_CODEC_ID_REALTEXT; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + while (!url_feof(s->pb)) { + AVPacket *sub; + const int64_t pos = avio_tell(s->pb) - (c != 0); + int n = ff_smil_extract_next_chunk(s->pb, &buf, &c); + + if (n == 0) + break; + + if (!av_strncasecmp(buf.str, "codec->extradata = av_strdup(buf.str); + if (!st->codec->extradata) { + res = AVERROR(ENOMEM); + goto end; + } + st->codec->extradata_size = buf.len + 1; + } else { + /* if we just read a