mirror of https://github.com/odrling/Aegisub
Compare commits
403 Commits
Author | SHA1 | Date |
---|---|---|
Niels Martin Hansen | 41d6719278 | |
Niels Martin Hansen | 26d3877bcf | |
Niels Martin Hansen | 81515f024f | |
Karl Blomster | 26d07c7aea | |
Niels Martin Hansen | ccd001fbbd | |
Niels Martin Hansen | 28bf657fda | |
Amar Takhar | 712fd197c3 | |
Niels Martin Hansen | 27bb28190b | |
Niels Martin Hansen | eaf507ca97 | |
Amar Takhar | d298d21e5d | |
Niels Martin Hansen | 9bc0d84862 | |
Amar Takhar | 32c6f54be7 | |
Niels Martin Hansen | 0e22e6dfd9 | |
Amar Takhar | 9bfdc30102 | |
Amar Takhar | cc4fd946d8 | |
Amar Takhar | c0c9d99700 | |
Karl Blomster | 83960dcce3 | |
Amar Takhar | ccf07d8a8a | |
Amar Takhar | 7455f77ad3 | |
Niels Martin Hansen | 7bf71d3ee3 | |
Niels Martin Hansen | d8161a9071 | |
Niels Martin Hansen | a78bbf6323 | |
Niels Martin Hansen | d02b75b199 | |
Karl Blomster | 3dc40e5c34 | |
Karl Blomster | 80a8ea634b | |
Amar Takhar | 9074ebec70 | |
Thomas Goyne | 21d675bd7b | |
Amar Takhar | 10041ef502 | |
Amar Takhar | ea5a3b7a73 | |
Amar Takhar | d35712946e | |
Amar Takhar | 516db6c901 | |
Amar Takhar | 1ec77b7576 | |
Karl Blomster | a9b479bd53 | |
harukalover | d1e5ff32e6 | |
Amar Takhar | 493f219a7e | |
Thomas Goyne | 1033427265 | |
harukalover | 4dc14ed973 | |
Thomas Goyne | 6e3b30c43b | |
harukalover | 61c0cbcb17 | |
harukalover | c2d89e8898 | |
harukalover | 49423c613e | |
Karl Blomster | 1c630e60cf | |
harukalover | 47f0a51e87 | |
harukalover | 9dcd3add79 | |
Amar Takhar | 4952b5168d | |
Amar Takhar | 085dd94f0d | |
Niels Martin Hansen | 4aafee2ac9 | |
Niels Martin Hansen | 0367fd37d4 | |
Karl Blomster | 86a1f2931b | |
Karl Blomster | f2415a4d65 | |
Karl Blomster | 2434fbd70a | |
harukalover | 17af911fa6 | |
Niels Martin Hansen | 69ed7c4e3e | |
Niels Martin Hansen | 6bff95373a | |
Thomas Goyne | 2bcdeb0692 | |
Niels Martin Hansen | 5dddb9dc9a | |
Niels Martin Hansen | 3be2db2037 | |
Niels Martin Hansen | 580341ea26 | |
harukalover | e0ac4c7490 | |
Amar Takhar | 1a85b179dc | |
Niels Martin Hansen | fd87b3efe7 | |
Amar Takhar | 8cd33bd57f | |
Niels Martin Hansen | 6b0b37039d | |
Niels Martin Hansen | 485881fb7c | |
Niels Martin Hansen | 7654458b0f | |
Niels Martin Hansen | dbf286136e | |
Niels Martin Hansen | 688cea62f4 | |
Niels Martin Hansen | 293f6fb32e | |
Niels Martin Hansen | 5cae92b4ae | |
Niels Martin Hansen | 8329868894 | |
Niels Martin Hansen | 5f097338e4 | |
Niels Martin Hansen | c31b39952a | |
Niels Martin Hansen | dac09935ae | |
Niels Martin Hansen | 624c81c0b2 | |
Niels Martin Hansen | 52271d6a56 | |
harukalover | 33d15e8c09 | |
Niels Martin Hansen | 022c948205 | |
Amar Takhar | 58862610bc | |
Niels Martin Hansen | 9d51917ac5 | |
Karl Blomster | db2ba854dc | |
Niels Martin Hansen | 03a2a70c92 | |
harukalover | 3568774cab | |
harukalover | 0ff5b0194e | |
harukalover | 1a44455205 | |
harukalover | be9a4a8319 | |
Amar Takhar | 9654e58881 | |
harukalover | bf1ddf4f7b | |
harukalover | de26551082 | |
Amar Takhar | 905b41dd5a | |
Amar Takhar | 322467e32b | |
harukalover | bc3b986df7 | |
Amar Takhar | 816f77e192 | |
Amar Takhar | 14e0a51876 | |
Amar Takhar | f1c598ef58 | |
Amar Takhar | e0beddbaf0 | |
Amar Takhar | 3e0ef96103 | |
Amar Takhar | 1006a36539 | |
Niels Martin Hansen | 6bbbe474b4 | |
Niels Martin Hansen | 9c79258f6c | |
Niels Martin Hansen | b2ff338dd8 | |
Niels Martin Hansen | 1c9f3f9ce0 | |
Niels Martin Hansen | 5a162e53bf | |
harukalover | f73c129b55 | |
Niels Martin Hansen | 513fcb4121 | |
Niels Martin Hansen | e4ef6ebf72 | |
Amar Takhar | 72b94648e8 | |
Niels Martin Hansen | 9e931f341c | |
Niels Martin Hansen | d1fe4bc8ea | |
Niels Martin Hansen | e3d10e1896 | |
Niels Martin Hansen | ba3df8b4e5 | |
Niels Martin Hansen | 8be4101d70 | |
Niels Martin Hansen | 286ffcfaae | |
Niels Martin Hansen | 8b9e3844a6 | |
Niels Martin Hansen | 4e00153303 | |
Niels Martin Hansen | 3ca48f660e | |
Niels Martin Hansen | 62671f8f7a | |
Niels Martin Hansen | 3ce395544f | |
Niels Martin Hansen | d1251757ef | |
Niels Martin Hansen | cf9fb5ae9a | |
Amar Takhar | 222f075778 | |
Niels Martin Hansen | a1f1514038 | |
Niels Martin Hansen | 08e0c2373c | |
Niels Martin Hansen | 332cd7b42e | |
Niels Martin Hansen | 8212f6944b | |
harukalover | 016ed435b1 | |
harukalover | 5c89ff4a50 | |
Fredrik Mellbin | ec297a5f11 | |
Amar Takhar | 5fa6858d92 | |
Amar Takhar | d21292667a | |
Amar Takhar | f6198f7fe8 | |
Amar Takhar | 4783530e9f | |
Niels Martin Hansen | 703ae3f7a6 | |
Niels Martin Hansen | 721d8aad79 | |
Niels Martin Hansen | 66821aa145 | |
Niels Martin Hansen | 49ba78c08e | |
Niels Martin Hansen | d7d07dc797 | |
Karl Blomster | 9e22e1456c | |
Fredrik Mellbin | 72bb9214fb | |
Fredrik Mellbin | 974bad3b6e | |
Fredrik Mellbin | 02065a7509 | |
Fredrik Mellbin | 38c420c7bd | |
harukalover | 68a7d04f06 | |
Karl Blomster | 114bc5b367 | |
Karl Blomster | 4ba221a931 | |
Fredrik Mellbin | 64242fd363 | |
Karl Blomster | 5a193a73ec | |
Karl Blomster | 3a947259f7 | |
Fredrik Mellbin | 5857345b8d | |
Karl Blomster | c60b4b449f | |
Niels Martin Hansen | 29a9d1fcf1 | |
Fredrik Mellbin | 12482ac418 | |
Karl Blomster | 72dc17af28 | |
Fredrik Mellbin | 45dc8d8819 | |
Fredrik Mellbin | da8163055c | |
Fredrik Mellbin | 3a7618cd64 | |
Fredrik Mellbin | cb1a9ba0b3 | |
Fredrik Mellbin | 24c0a673b9 | |
Karl Blomster | edc10f752c | |
Karl Blomster | 6b42083504 | |
Fredrik Mellbin | d2c469e3e0 | |
Niels Martin Hansen | 8a04053ff2 | |
harukalover | 9550bc6366 | |
harukalover | 8a417de164 | |
Niels Martin Hansen | d40b0f36f6 | |
harukalover | 82f02efba8 | |
harukalover | b4a250122a | |
harukalover | e771de5483 | |
harukalover | b5d7851413 | |
harukalover | eadf49d10e | |
harukalover | bfce57662e | |
harukalover | 68d705fd22 | |
Fredrik Mellbin | 73dbcf6ecd | |
Fredrik Mellbin | 7b0efd10f8 | |
Niels Martin Hansen | 4f14cddf4f | |
Fredrik Mellbin | 3d2ad9525a | |
Fredrik Mellbin | 9d636c3cfb | |
Fredrik Mellbin | cd7932d6ee | |
harukalover | 1e52702f0a | |
Niels Martin Hansen | bd485315a8 | |
harukalover | bace72cf29 | |
harukalover | 14e3cb4323 | |
harukalover | 55f4a80904 | |
harukalover | 2feb39393d | |
Karl Blomster | ea8841932b | |
Fredrik Mellbin | fdf73126b9 | |
Karl Blomster | cf96f7c8ad | |
harukalover | 5b9bd7ac6d | |
Niels Martin Hansen | a60d16913d | |
Niels Martin Hansen | d6cea9d93e | |
Niels Martin Hansen | 741c3fbd4e | |
Amar Takhar | e5eb667879 | |
Fredrik Mellbin | 80a209e95a | |
Niels Martin Hansen | 6a999030f6 | |
Niels Martin Hansen | 0d1b45f7d1 | |
Karl Blomster | 1dbb23f106 | |
Karl Blomster | 5e214986e1 | |
harukalover | 3011ade060 | |
Niels Martin Hansen | 3ce80afadf | |
Niels Martin Hansen | 6e6447f215 | |
Niels Martin Hansen | a8bd30487b | |
Niels Martin Hansen | c7c95d20c0 | |
Niels Martin Hansen | 153cfbcd5f | |
Niels Martin Hansen | 20477198e6 | |
Niels Martin Hansen | 33696616f4 | |
Niels Martin Hansen | b19d604de4 | |
Niels Martin Hansen | 00df76c4fb | |
Niels Martin Hansen | ef03e3050d | |
harukalover | 82fca6e35f | |
harukalover | 370b986181 | |
Niels Martin Hansen | 5a4ffa1005 | |
harukalover | d8e6d94754 | |
Karl Blomster | 6a42e17409 | |
Karl Blomster | 887b9b1988 | |
Fredrik Mellbin | 16f3f59cb2 | |
Niels Martin Hansen | ae988ed37b | |
Niels Martin Hansen | f425b58017 | |
Niels Martin Hansen | e87a031828 | |
Niels Martin Hansen | 86e70e9094 | |
Niels Martin Hansen | 7e90cdcf09 | |
Niels Martin Hansen | 95d71d1dcf | |
Niels Martin Hansen | cad5b98e1b | |
Niels Martin Hansen | 54037c56e7 | |
Karl Blomster | d56982ed4a | |
Karl Blomster | 179d23fd5c | |
Fredrik Mellbin | 2cab187fd8 | |
Karl Blomster | 3bdbcd7b5e | |
Karl Blomster | 87a39833b8 | |
Karl Blomster | bfe40a6667 | |
Karl Blomster | c6fe68c76d | |
Karl Blomster | 02f7d0006c | |
Niels Martin Hansen | 36dbd86dd6 | |
Amar Takhar | c9affe9aeb | |
Amar Takhar | 8c8a0abe38 | |
Amar Takhar | f893e8a29f | |
Amar Takhar | 5b356f2755 | |
Amar Takhar | 2090994256 | |
Amar Takhar | b00e485149 | |
Fredrik Mellbin | 0a221706d5 | |
Karl Blomster | 869e5c7594 | |
Karl Blomster | e3ce74248d | |
Karl Blomster | 45627548d6 | |
Fredrik Mellbin | 487dc50409 | |
Amar Takhar | 1cbd29531c | |
Niels Martin Hansen | b840c9f97e | |
Karl Blomster | 4445d812ce | |
Amar Takhar | a9e1f80c8c | |
Fredrik Mellbin | fad9a58240 | |
Karl Blomster | 2f7b62d206 | |
Niels Martin Hansen | f0b442f4b4 | |
Niels Martin Hansen | a2822356bf | |
Karl Blomster | 1f062a5553 | |
Karl Blomster | 9c93aeb179 | |
Fredrik Mellbin | 27e90e973f | |
Karl Blomster | 8bc3a39d0c | |
Amar Takhar | 29721b983e | |
Amar Takhar | 1fb14af113 | |
Amar Takhar | 05af876c1d | |
Amar Takhar | 02210e3669 | |
Amar Takhar | d769707641 | |
Amar Takhar | 3267c11fed | |
Karl Blomster | 876f726a6d | |
Amar Takhar | 800e57897b | |
Amar Takhar | 9774fbb1c8 | |
Amar Takhar | 77643ae596 | |
Amar Takhar | 9079f1576f | |
Amar Takhar | 8a13a78229 | |
Amar Takhar | d929e60b4c | |
Amar Takhar | 501782d605 | |
Amar Takhar | bf99cbcd58 | |
Niels Martin Hansen | 03ec2ae033 | |
Karl Blomster | 9444ebd0d1 | |
Karl Blomster | 2ba20b12c8 | |
Karl Blomster | fe05adb64e | |
Karl Blomster | c8908fd2d1 | |
Karl Blomster | 2d11cb6769 | |
Karl Blomster | 78736ce617 | |
Karl Blomster | 8c5e380264 | |
Amar Takhar | 8d11b72059 | |
Fredrik Mellbin | bc46304aa1 | |
Fredrik Mellbin | 3655fbb7fa | |
Fredrik Mellbin | 4c4df5f17a | |
Fredrik Mellbin | 61d14a7b89 | |
Fredrik Mellbin | 4e7bff9bd6 | |
Amar Takhar | ac63f69e06 | |
Amar Takhar | dbb192b01a | |
Fredrik Mellbin | 4b74827df6 | |
Karl Blomster | 518678b930 | |
Fredrik Mellbin | b8cf6fce8e | |
Amar Takhar | 4777905b0a | |
Amar Takhar | 80ce8b4d77 | |
Amar Takhar | bcf82c0f9f | |
Karl Blomster | b97bbf9792 | |
Karl Blomster | 22faf80cdb | |
Fredrik Mellbin | bd0f1dcd10 | |
Amar Takhar | 71c29969a3 | |
Amar Takhar | 09d32d8151 | |
Amar Takhar | 1118dcac4a | |
Amar Takhar | f5124e7763 | |
Niels Martin Hansen | e8d1767428 | |
Niels Martin Hansen | b842a3ea76 | |
Niels Martin Hansen | 58ec137621 | |
Niels Martin Hansen | 5a50efac9f | |
Niels Martin Hansen | 30422e5ca8 | |
Karl Blomster | 254a7ae359 | |
Karl Blomster | c806ba8d72 | |
Karl Blomster | 3e70c0af81 | |
Karl Blomster | f12a3a8af7 | |
Niels Martin Hansen | 4787a2fcdb | |
Niels Martin Hansen | ffde7daec5 | |
Niels Martin Hansen | 4fc3b7faf0 | |
Niels Martin Hansen | af538651ef | |
Niels Martin Hansen | 2c392407c4 | |
Niels Martin Hansen | 6d1207900b | |
Niels Martin Hansen | a3600f8ca1 | |
Niels Martin Hansen | 2d64f5079f | |
Niels Martin Hansen | e977f3c991 | |
Karl Blomster | 59eb07c79f | |
Karl Blomster | 80ce8e7508 | |
Karl Blomster | 6a69da3331 | |
Karl Blomster | 13c59b5af0 | |
Niels Martin Hansen | 19b5544a2a | |
Amar Takhar | 28470e309d | |
Karl Blomster | 0ad0b6d5c9 | |
Amar Takhar | 235af35603 | |
Amar Takhar | a3f9735d38 | |
Amar Takhar | 0d7af5f333 | |
Amar Takhar | 89fa60082e | |
Amar Takhar | eebde56363 | |
Niels Martin Hansen | bcc8cede88 | |
Niels Martin Hansen | 103c407a64 | |
Amar Takhar | fd16c4ff13 | |
Amar Takhar | d689a486c0 | |
Amar Takhar | 96a33b1331 | |
Amar Takhar | 218c9877ac | |
Amar Takhar | af84de21ab | |
Niels Martin Hansen | 50679014ea | |
Amar Takhar | e6a09d383c | |
Amar Takhar | 41034545e7 | |
Amar Takhar | 55afc7c29a | |
Amar Takhar | fb4d7e67e2 | |
Amar Takhar | 11c341030b | |
Amar Takhar | f6d8cca7aa | |
Amar Takhar | 72bfc9862a | |
Amar Takhar | c674034054 | |
Amar Takhar | 2c3e71b098 | |
Amar Takhar | 7ecdd1035a | |
Amar Takhar | ebdeeec267 | |
Amar Takhar | 907484a602 | |
Amar Takhar | c12f35f9c6 | |
Fredrik Mellbin | 6ca864b88c | |
Fredrik Mellbin | cae545b1eb | |
Amar Takhar | 8631bd42e1 | |
Amar Takhar | bf5146f937 | |
Amar Takhar | 77a4983219 | |
Amar Takhar | 00339fefd0 | |
Karl Blomster | ff1a4fb8fc | |
Karl Blomster | eed309f187 | |
Karl Blomster | f80421d2e6 | |
Karl Blomster | ac2c8bbed6 | |
Karl Blomster | 72cc224f5b | |
Karl Blomster | 6bb861c7ef | |
Karl Blomster | 546f41e1a3 | |
Karl Blomster | 89c465dc71 | |
Rodrigo Braz Monteiro | 261d9c21f1 | |
Amar Takhar | c6e55826f5 | |
Amar Takhar | 67af0c3661 | |
Amar Takhar | 6f090f9027 | |
Amar Takhar | 4c02eb0984 | |
Amar Takhar | 514624f83a | |
Amar Takhar | 908e9381e8 | |
Amar Takhar | 3df8846bcb | |
Amar Takhar | d7e023e57f | |
Amar Takhar | 27cdb7d7a4 | |
Amar Takhar | aa506bfe40 | |
Amar Takhar | b8d6be1ee3 | |
Amar Takhar | 0df92fa359 | |
Amar Takhar | a067bd560d | |
Amar Takhar | 563dc4689e | |
Niels Martin Hansen | 5e476ca821 | |
Amar Takhar | ae9a23d848 | |
Amar Takhar | 10a531b684 | |
Amar Takhar | eac9424b2a | |
Amar Takhar | 9df096a660 | |
Amar Takhar | 68a9d536cb | |
Niels Martin Hansen | c2f3b95fa3 | |
Amar Takhar | c4d4e6db98 | |
Amar Takhar | e7d5448885 | |
Amar Takhar | ae6541cde4 | |
Amar Takhar | 5be76ad6ac | |
Amar Takhar | e792da082c | |
Amar Takhar | 8fab7c9ddc | |
Amar Takhar | ea31698587 | |
Amar Takhar | 83bf4c7512 | |
Amar Takhar | cf936f5e12 | |
Amar Takhar | b10f0503ba | |
Amar Takhar | ef269acdd3 | |
Amar Takhar | acea1a7e35 | |
Amar Takhar | 93c073cbc6 | |
Amar Takhar | fa6831c1b0 | |
Amar Takhar | fce6a3716a | |
Niels Martin Hansen | 342ab23e88 | |
Niels Martin Hansen | fe2e4ba1e4 | |
http://greg.geekmind.org/mplayer/mplayer-libass-fixes.txt | 8cb2492a02 |
|
@ -1,32 +0,0 @@
|
|||
noinst_LIBRARIES = libffmpegsource2_aegisub.a
|
||||
|
||||
AM_CPPFLAGS = -I../aegisub -D__UNIX__ -DHAVE_STRLCPY @LIBAVFORMAT_CFLAGS@ @LIBAVCODEC_CFLAGS@ @LIBSWSCALE_CFLAGS@ @LIBAVUTIL_CFLAGS@ @LIBPOSTPROC_CFLAGS@
|
||||
|
||||
|
||||
libffmpegsource2_aegisub_a_SOURCES = \
|
||||
ffaudiosource.cpp \
|
||||
ffms.cpp \
|
||||
ffvideosource.cpp \
|
||||
indexing.cpp \
|
||||
MatroskaParser.c \
|
||||
stdiostream.c \
|
||||
utils.cpp \
|
||||
wave64writer.cpp
|
||||
|
||||
|
||||
libffmpegsource2_aegisub_a_SOURCES += \
|
||||
MatroskaParser.h \
|
||||
avisynth.h \
|
||||
ffaudiosource.h \
|
||||
ffavisynth.h \
|
||||
ffms.h \
|
||||
ffpp.h \
|
||||
ffswscale.h \
|
||||
ffvideosource.h \
|
||||
indexing.h \
|
||||
stdiostream.h \
|
||||
utils.h \
|
||||
wave64writer.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
ffms2.html
|
|
@ -1,749 +0,0 @@
|
|||
// Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
|
||||
// http://www.avisynth.org
|
||||
|
||||
// This program 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.
|
||||
//
|
||||
// This program is distributed in the hope that 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
|
||||
// http://www.gnu.org/copyleft/gpl.html .
|
||||
//
|
||||
// Linking Avisynth statically or dynamically with other modules is making a
|
||||
// combined work based on Avisynth. Thus, the terms and conditions of the GNU
|
||||
// General Public License cover the whole combination.
|
||||
//
|
||||
// As a special exception, the copyright holders of Avisynth give you
|
||||
// permission to link Avisynth with independent modules that communicate with
|
||||
// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
|
||||
// terms of these independent modules, and to copy and distribute the
|
||||
// resulting combined work under terms of your choice, provided that
|
||||
// every copy of the combined work is accompanied by a complete copy of
|
||||
// the source code of Avisynth (the version of Avisynth used to produce the
|
||||
// combined work), being distributed under the terms of the GNU General
|
||||
// Public License plus this exception. An independent module is a module
|
||||
// which is not derived from or based on Avisynth, such as 3rd-party filters,
|
||||
// import and export plugins, or graphical user interfaces.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AVISYNTH_H__
|
||||
#define __AVISYNTH_H__
|
||||
|
||||
enum { AVISYNTH_INTERFACE_VERSION = 3 };
|
||||
|
||||
|
||||
/* Define all types necessary for interfacing with avisynth.dll
|
||||
Moved from internal.h */
|
||||
|
||||
// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc.
|
||||
#include <windef.h>
|
||||
|
||||
// COM interface macros
|
||||
#include <objbase.h>
|
||||
|
||||
|
||||
// Raster types used by VirtualDub & Avisynth
|
||||
#define in64 (__int64)(unsigned short)
|
||||
typedef unsigned long Pixel; // this will break on 64-bit machines!
|
||||
typedef unsigned long Pixel32;
|
||||
typedef unsigned char Pixel8;
|
||||
typedef long PixCoord;
|
||||
typedef long PixDim;
|
||||
typedef long PixOffset;
|
||||
|
||||
|
||||
/* Compiler-specific crap */
|
||||
|
||||
// Tell MSVC to stop precompiling here
|
||||
#ifdef _MSC_VER
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
// Set up debugging macros for MS compilers; for others, step down to the
|
||||
// standard <assert.h> interface
|
||||
#ifdef _MSC_VER
|
||||
#include <crtdbg.h>
|
||||
#else
|
||||
#define _RPT0(a,b) ((void)0)
|
||||
#define _RPT1(a,b,c) ((void)0)
|
||||
#define _RPT2(a,b,c,d) ((void)0)
|
||||
#define _RPT3(a,b,c,d,e) ((void)0)
|
||||
#define _RPT4(a,b,c,d,e,f) ((void)0)
|
||||
|
||||
#define _ASSERTE(x) assert(x)
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// I had problems with Premiere wanting 1-byte alignment for its structures,
|
||||
// so I now set the Avisynth struct alignment explicitly here.
|
||||
#pragma pack(push,8)
|
||||
|
||||
#define FRAME_ALIGN 16
|
||||
// Default frame alignment is 16 bytes, to help P4, when using SSE2
|
||||
|
||||
// The VideoInfo struct holds global information about a clip (i.e.
|
||||
// information that does not depend on the frame number). The GetVideoInfo
|
||||
// method in IClip returns this struct.
|
||||
|
||||
// Audio Sample information
|
||||
typedef float SFLOAT;
|
||||
|
||||
enum {SAMPLE_INT8 = 1<<0,
|
||||
SAMPLE_INT16 = 1<<1,
|
||||
SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware.
|
||||
SAMPLE_INT32 = 1<<3,
|
||||
SAMPLE_FLOAT = 1<<4};
|
||||
|
||||
enum {
|
||||
PLANAR_Y=1<<0,
|
||||
PLANAR_U=1<<1,
|
||||
PLANAR_V=1<<2,
|
||||
PLANAR_ALIGNED=1<<3,
|
||||
PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED,
|
||||
PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED,
|
||||
PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED,
|
||||
};
|
||||
|
||||
struct VideoInfo {
|
||||
int width, height; // width=0 means no video
|
||||
unsigned fps_numerator, fps_denominator;
|
||||
int num_frames;
|
||||
// This is more extensible than previous versions. More properties can be added seeminglesly.
|
||||
|
||||
// Colorspace properties.
|
||||
enum {
|
||||
CS_BGR = 1<<28,
|
||||
CS_YUV = 1<<29,
|
||||
CS_INTERLEAVED = 1<<30,
|
||||
CS_PLANAR = 1<<31
|
||||
};
|
||||
|
||||
// Specific colorformats
|
||||
enum { CS_UNKNOWN = 0,
|
||||
CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED,
|
||||
CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED,
|
||||
CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED,
|
||||
CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR, // y-v-u, planar
|
||||
CS_I420 = 1<<4 | CS_YUV | CS_PLANAR, // y-u-v, planar
|
||||
CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR // same as above
|
||||
};
|
||||
int pixel_type; // changed to int as of 2.5
|
||||
|
||||
|
||||
int audio_samples_per_second; // 0 means no audio
|
||||
int sample_type; // as of 2.5
|
||||
__int64 num_audio_samples; // changed as of 2.5
|
||||
int nchannels; // as of 2.5
|
||||
|
||||
// Imagetype properties
|
||||
|
||||
int image_type;
|
||||
|
||||
enum {
|
||||
IT_BFF = 1<<0,
|
||||
IT_TFF = 1<<1,
|
||||
IT_FIELDBASED = 1<<2
|
||||
};
|
||||
|
||||
// useful functions of the above
|
||||
bool HasVideo() const { return (width!=0); }
|
||||
bool HasAudio() const { return (audio_samples_per_second!=0); }
|
||||
bool IsRGB() const { return !!(pixel_type&CS_BGR); }
|
||||
bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties
|
||||
bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; }
|
||||
bool IsYUV() const { return !!(pixel_type&CS_YUV ); }
|
||||
bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; }
|
||||
bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); }
|
||||
bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); }
|
||||
bool Is(int property) const { return ((pixel_type & property)==property ); }
|
||||
bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); }
|
||||
bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); }
|
||||
bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF|IT_TFF))); }
|
||||
bool IsBFF() const { return !!(image_type & IT_BFF); }
|
||||
bool IsTFF() const { return !!(image_type & IT_TFF); }
|
||||
|
||||
bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); } // Don't use this
|
||||
int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); } // Will not work on planar images, but will return only luma planes
|
||||
int RowSize() const { return BytesFromPixels(width); } // Also only returns first plane on planar images
|
||||
int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p; } return height * ((RowSize()+3) & ~3); }
|
||||
__int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; }
|
||||
int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator)/((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; }
|
||||
__int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; }
|
||||
__int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); }
|
||||
int AudioChannels() const { return HasAudio() ? nchannels : 0; }
|
||||
int SampleType() const{ return sample_type;}
|
||||
bool IsSampleType(int testtype) const{ return !!(sample_type&testtype);}
|
||||
int SamplesPerSecond() const { return audio_samples_per_second; }
|
||||
int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();}
|
||||
void SetFieldBased(bool isfieldbased) { if (isfieldbased) image_type|=IT_FIELDBASED; else image_type&=~IT_FIELDBASED; }
|
||||
void Set(int property) { image_type|=property; }
|
||||
void Clear(int property) { image_type&=~property; }
|
||||
|
||||
int BitsPerPixel() const {
|
||||
switch (pixel_type) {
|
||||
case CS_BGR24:
|
||||
return 24;
|
||||
case CS_BGR32:
|
||||
return 32;
|
||||
case CS_YUY2:
|
||||
return 16;
|
||||
case CS_YV12:
|
||||
case CS_I420:
|
||||
return 12;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int BytesPerChannelSample() const {
|
||||
switch (sample_type) {
|
||||
case SAMPLE_INT8:
|
||||
return sizeof(signed char);
|
||||
case SAMPLE_INT16:
|
||||
return sizeof(signed short);
|
||||
case SAMPLE_INT24:
|
||||
return 3;
|
||||
case SAMPLE_INT32:
|
||||
return sizeof(signed int);
|
||||
case SAMPLE_FLOAT:
|
||||
return sizeof(SFLOAT);
|
||||
default:
|
||||
_ASSERTE("Sample type not recognized!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// useful mutator
|
||||
void SetFPS(unsigned numerator, unsigned denominator) {
|
||||
if ((numerator == 0) || (denominator == 0)) {
|
||||
fps_numerator = 0;
|
||||
fps_denominator = 1;
|
||||
}
|
||||
else {
|
||||
unsigned x=numerator, y=denominator;
|
||||
while (y) { // find gcd
|
||||
unsigned t = x%y; x = y; y = t;
|
||||
}
|
||||
fps_numerator = numerator/x;
|
||||
fps_denominator = denominator/x;
|
||||
}
|
||||
}
|
||||
|
||||
// Range protected multiply-divide of FPS
|
||||
void MulDivFPS(unsigned multiplier, unsigned divisor) {
|
||||
unsigned __int64 numerator = UInt32x32To64(fps_numerator, multiplier);
|
||||
unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor);
|
||||
|
||||
unsigned __int64 x=numerator, y=denominator;
|
||||
while (y) { // find gcd
|
||||
unsigned __int64 t = x%y; x = y; y = t;
|
||||
}
|
||||
numerator /= x; // normalize
|
||||
denominator /= x;
|
||||
|
||||
unsigned __int64 temp = numerator | denominator; // Just looking top bit
|
||||
unsigned u = 0;
|
||||
while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2
|
||||
temp = Int64ShrlMod32(temp, 1);
|
||||
u++;
|
||||
}
|
||||
if (u) { // Scale to fit
|
||||
const unsigned round = 1 << (u-1);
|
||||
SetFPS( (unsigned)Int64ShrlMod32(numerator + round, u),
|
||||
(unsigned)Int64ShrlMod32(denominator + round, u) );
|
||||
}
|
||||
else {
|
||||
fps_numerator = (unsigned)numerator;
|
||||
fps_denominator = (unsigned)denominator;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for same colorspace
|
||||
bool IsSameColorspace(const VideoInfo& vi) const {
|
||||
if (vi.pixel_type == pixel_type) return TRUE;
|
||||
if (IsYV12() && vi.IsYV12()) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// VideoFrameBuffer holds information about a memory block which is used
|
||||
// for video data. For efficiency, instances of this class are not deleted
|
||||
// when the refcount reaches zero; instead they're stored in a linked list
|
||||
// to be reused. The instances are deleted when the corresponding AVS
|
||||
// file is closed.
|
||||
|
||||
class VideoFrameBuffer {
|
||||
BYTE* const data;
|
||||
const int data_size;
|
||||
// sequence_number is incremented every time the buffer is changed, so
|
||||
// that stale views can tell they're no longer valid.
|
||||
long sequence_number;
|
||||
|
||||
friend class VideoFrame;
|
||||
friend class Cache;
|
||||
friend class ScriptEnvironment;
|
||||
long refcount;
|
||||
|
||||
public:
|
||||
VideoFrameBuffer(int size);
|
||||
VideoFrameBuffer();
|
||||
~VideoFrameBuffer();
|
||||
|
||||
const BYTE* GetReadPtr() const { return data; }
|
||||
BYTE* GetWritePtr() { ++sequence_number; return data; }
|
||||
int GetDataSize() { return data_size; }
|
||||
int GetSequenceNumber() { return sequence_number; }
|
||||
int GetRefcount() { return refcount; }
|
||||
};
|
||||
|
||||
|
||||
class IClip;
|
||||
class PClip;
|
||||
class PVideoFrame;
|
||||
class IScriptEnvironment;
|
||||
class AVSValue;
|
||||
|
||||
|
||||
// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new
|
||||
// is overloaded to recycle class instances.
|
||||
|
||||
class VideoFrame {
|
||||
int refcount;
|
||||
VideoFrameBuffer* const vfb;
|
||||
const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture.
|
||||
|
||||
friend class PVideoFrame;
|
||||
void AddRef() { InterlockedIncrement((long *)&refcount); }
|
||||
void Release() { if (refcount==1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); }
|
||||
|
||||
friend class ScriptEnvironment;
|
||||
friend class Cache;
|
||||
|
||||
VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height);
|
||||
VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV);
|
||||
|
||||
void* operator new(unsigned size);
|
||||
// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard!
|
||||
public:
|
||||
int GetPitch() const { return pitch; }
|
||||
int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; }
|
||||
int GetRowSize() const { return row_size; }
|
||||
int GetRowSize(int plane) const {
|
||||
switch (plane) {
|
||||
case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0;
|
||||
case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED:
|
||||
if (pitchUV) {
|
||||
int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize
|
||||
if (r<=pitchUV)
|
||||
return r;
|
||||
return row_size>>1;
|
||||
} else return 0;
|
||||
case PLANAR_Y_ALIGNED:
|
||||
int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize
|
||||
if (r<=pitch)
|
||||
return r;
|
||||
return row_size;
|
||||
}
|
||||
return row_size; }
|
||||
int GetHeight() const { return height; }
|
||||
int GetHeight(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; }
|
||||
|
||||
// generally you shouldn't use these three
|
||||
VideoFrameBuffer* GetFrameBuffer() const { return vfb; }
|
||||
int GetOffset() const { return offset; }
|
||||
int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; }
|
||||
|
||||
// in plugins use env->SubFrame()
|
||||
VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
|
||||
VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const;
|
||||
|
||||
|
||||
const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; }
|
||||
const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); }
|
||||
|
||||
bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
|
||||
|
||||
BYTE* GetWritePtr() const {
|
||||
if (vfb->GetRefcount()>1) {
|
||||
_ASSERT(FALSE);
|
||||
//throw AvisynthError("Internal Error - refcount was more than one!");
|
||||
}
|
||||
return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
|
||||
}
|
||||
|
||||
BYTE* GetWritePtr(int plane) const {
|
||||
if (plane==PLANAR_Y) {
|
||||
if (vfb->GetRefcount()>1) {
|
||||
_ASSERT(FALSE);
|
||||
// throw AvisynthError("Internal Error - refcount was more than one!");
|
||||
}
|
||||
return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0;
|
||||
}
|
||||
return vfb->data + GetOffset(plane);
|
||||
}
|
||||
|
||||
~VideoFrame() { InterlockedDecrement(&vfb->refcount); }
|
||||
};
|
||||
|
||||
enum {
|
||||
CACHE_NOTHING=0,
|
||||
CACHE_RANGE=1,
|
||||
CACHE_ALL=2,
|
||||
CACHE_AUDIO=3,
|
||||
CACHE_AUDIO_NONE=4
|
||||
};
|
||||
|
||||
// Base class for all filters.
|
||||
class IClip {
|
||||
friend class PClip;
|
||||
friend class AVSValue;
|
||||
int refcnt;
|
||||
void AddRef() { InterlockedIncrement((long *)&refcnt); }
|
||||
void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; }
|
||||
public:
|
||||
IClip() : refcnt(0) {}
|
||||
|
||||
virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
|
||||
|
||||
virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
|
||||
virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame
|
||||
virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples
|
||||
virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter.
|
||||
virtual const VideoInfo& __stdcall GetVideoInfo() = 0;
|
||||
virtual __stdcall ~IClip() {}
|
||||
};
|
||||
|
||||
|
||||
// smart pointer to IClip
|
||||
class PClip {
|
||||
|
||||
IClip* p;
|
||||
|
||||
IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
|
||||
friend class AVSValue;
|
||||
friend class VideoFrame;
|
||||
|
||||
void Init(IClip* x) {
|
||||
if (x) x->AddRef();
|
||||
p=x;
|
||||
}
|
||||
void Set(IClip* x) {
|
||||
if (x) x->AddRef();
|
||||
if (p) p->Release();
|
||||
p=x;
|
||||
}
|
||||
|
||||
public:
|
||||
PClip() { p = 0; }
|
||||
PClip(const PClip& x) { Init(x.p); }
|
||||
PClip(IClip* x) { Init(x); }
|
||||
void operator=(IClip* x) { Set(x); }
|
||||
void operator=(const PClip& x) { Set(x.p); }
|
||||
|
||||
IClip* operator->() const { return p; }
|
||||
|
||||
// useful in conditional expressions
|
||||
operator void*() const { return p; }
|
||||
bool operator!() const { return !p; }
|
||||
|
||||
~PClip() { if (p) p->Release(); }
|
||||
};
|
||||
|
||||
|
||||
// smart pointer to VideoFrame
|
||||
class PVideoFrame {
|
||||
|
||||
VideoFrame* p;
|
||||
|
||||
void Init(VideoFrame* x) {
|
||||
if (x) x->AddRef();
|
||||
p=x;
|
||||
}
|
||||
void Set(VideoFrame* x) {
|
||||
if (x) x->AddRef();
|
||||
if (p) p->Release();
|
||||
p=x;
|
||||
}
|
||||
|
||||
public:
|
||||
PVideoFrame() { p = 0; }
|
||||
PVideoFrame(const PVideoFrame& x) { Init(x.p); }
|
||||
PVideoFrame(VideoFrame* x) { Init(x); }
|
||||
void operator=(VideoFrame* x) { Set(x); }
|
||||
void operator=(const PVideoFrame& x) { Set(x.p); }
|
||||
|
||||
VideoFrame* operator->() const { return p; }
|
||||
|
||||
// for conditional expressions
|
||||
operator void*() const { return p; }
|
||||
bool operator!() const { return !p; }
|
||||
|
||||
~PVideoFrame() { if (p) p->Release();}
|
||||
};
|
||||
|
||||
|
||||
class AVSValue {
|
||||
public:
|
||||
|
||||
AVSValue() { type = 'v'; }
|
||||
AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); }
|
||||
AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
|
||||
AVSValue(bool b) { type = 'b'; boolean = b; }
|
||||
AVSValue(int i) { type = 'i'; integer = i; }
|
||||
// AVSValue(__int64 l) { type = 'l'; longlong = l; }
|
||||
AVSValue(float f) { type = 'f'; floating_pt = f; }
|
||||
AVSValue(double f) { type = 'f'; floating_pt = float(f); }
|
||||
AVSValue(const char* s) { type = 's'; string = s; }
|
||||
AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; }
|
||||
AVSValue(const AVSValue& v) { Assign(&v, true); }
|
||||
|
||||
~AVSValue() { if (IsClip() && clip) clip->Release(); }
|
||||
AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; }
|
||||
|
||||
// Note that we transparently allow 'int' to be treated as 'float'.
|
||||
// There are no int<->bool conversions, though.
|
||||
|
||||
bool Defined() const { return type != 'v'; }
|
||||
bool IsClip() const { return type == 'c'; }
|
||||
bool IsBool() const { return type == 'b'; }
|
||||
bool IsInt() const { return type == 'i'; }
|
||||
// bool IsLong() const { return (type == 'l'|| type == 'i'); }
|
||||
bool IsFloat() const { return type == 'f' || type == 'i'; }
|
||||
bool IsString() const { return type == 's'; }
|
||||
bool IsArray() const { return type == 'a'; }
|
||||
|
||||
PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; }
|
||||
bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
|
||||
int AsInt() const { _ASSERTE(IsInt()); return integer; }
|
||||
// int AsLong() const { _ASSERTE(IsLong()); return longlong; }
|
||||
const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; }
|
||||
double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; }
|
||||
|
||||
bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; }
|
||||
int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; }
|
||||
double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; }
|
||||
const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; }
|
||||
|
||||
int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; }
|
||||
|
||||
const AVSValue& operator[](int index) const {
|
||||
_ASSERTE(IsArray() && index>=0 && index<array_size);
|
||||
return (IsArray() && index>=0 && index<array_size) ? array[index] : *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
|
||||
short array_size;
|
||||
union {
|
||||
IClip* clip;
|
||||
bool boolean;
|
||||
int integer;
|
||||
float floating_pt;
|
||||
const char* string;
|
||||
const AVSValue* array;
|
||||
// __int64 longlong;
|
||||
};
|
||||
|
||||
void Assign(const AVSValue* src, bool init) {
|
||||
if (src->IsClip() && src->clip)
|
||||
src->clip->AddRef();
|
||||
if (!init && IsClip() && clip)
|
||||
clip->Release();
|
||||
// make sure this copies the whole struct!
|
||||
((__int32*)this)[0] = ((__int32*)src)[0];
|
||||
((__int32*)this)[1] = ((__int32*)src)[1];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// instantiable null filter
|
||||
class GenericVideoFilter : public IClip {
|
||||
protected:
|
||||
PClip child;
|
||||
VideoInfo vi;
|
||||
public:
|
||||
GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
|
||||
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); }
|
||||
const VideoInfo& __stdcall GetVideoInfo() { return vi; }
|
||||
bool __stdcall GetParity(int n) { return child->GetParity(n); }
|
||||
void __stdcall SetCacheHints(int cachehints,int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter.
|
||||
};
|
||||
|
||||
|
||||
class AvisynthError /* exception */ {
|
||||
public:
|
||||
const char* const msg;
|
||||
AvisynthError(const char* _msg) : msg(_msg) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* Helper classes useful to plugin authors */
|
||||
|
||||
class AlignPlanar : public GenericVideoFilter
|
||||
{
|
||||
public:
|
||||
AlignPlanar(PClip _clip);
|
||||
static PClip Create(PClip clip);
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FillBorder : public GenericVideoFilter
|
||||
{
|
||||
public:
|
||||
FillBorder(PClip _clip);
|
||||
static PClip Create(PClip clip);
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ConvertAudio : public GenericVideoFilter
|
||||
/**
|
||||
* Helper class to convert audio to any format
|
||||
**/
|
||||
{
|
||||
public:
|
||||
ConvertAudio(PClip _clip, int prefered_format);
|
||||
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env);
|
||||
void __stdcall SetCacheHints(int cachehints,int frame_range); // We do pass cache requests upwards, to the cache!
|
||||
|
||||
static PClip Create(PClip clip, int sample_type, int prefered_type);
|
||||
static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_24bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*);
|
||||
virtual ~ConvertAudio();
|
||||
|
||||
private:
|
||||
void convertToFloat(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_3DN(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_SSE(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertToFloat_SSE2(char* inbuf, float* outbuf, char sample_type, int count);
|
||||
void convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_3DN(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_SSE(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
void convertFromFloat_SSE2(float* inbuf, void* outbuf, char sample_type, int count);
|
||||
|
||||
__inline int Saturate_int8(float n);
|
||||
__inline short Saturate_int16(float n);
|
||||
__inline int Saturate_int24(float n);
|
||||
__inline int Saturate_int32(float n);
|
||||
|
||||
char src_format;
|
||||
char dst_format;
|
||||
int src_bps;
|
||||
char *tempbuffer;
|
||||
SFLOAT *floatbuffer;
|
||||
int tempbuffer_size;
|
||||
};
|
||||
|
||||
|
||||
// For GetCPUFlags. These are backwards-compatible with those in VirtualDub.
|
||||
enum {
|
||||
/* slowest CPU to support extension */
|
||||
CPUF_FORCE = 0x01, // N/A
|
||||
CPUF_FPU = 0x02, // 386/486DX
|
||||
CPUF_MMX = 0x04, // P55C, K6, PII
|
||||
CPUF_INTEGER_SSE = 0x08, // PIII, Athlon
|
||||
CPUF_SSE = 0x10, // PIII, Athlon XP/MP
|
||||
CPUF_SSE2 = 0x20, // PIV, Hammer
|
||||
CPUF_3DNOW = 0x40, // K6-2
|
||||
CPUF_3DNOW_EXT = 0x80, // Athlon
|
||||
CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
|
||||
// will have anyway)
|
||||
CPUF_SSE3 = 0x100, // Some P4 & Athlon 64.
|
||||
};
|
||||
#define MAX_INT 0x7fffffff
|
||||
#define MIN_INT -0x7fffffff
|
||||
|
||||
|
||||
|
||||
class IScriptEnvironment {
|
||||
public:
|
||||
virtual __stdcall ~IScriptEnvironment() {}
|
||||
|
||||
virtual /*static*/ long __stdcall GetCPUFlags() = 0;
|
||||
|
||||
virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
|
||||
virtual char* __stdcall Sprintf(const char* fmt, ...) = 0;
|
||||
// note: val is really a va_list; I hope everyone typedefs va_list to a pointer
|
||||
virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0;
|
||||
|
||||
__declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0;
|
||||
|
||||
class NotFound /*exception*/ {}; // thrown by Invoke and GetVar
|
||||
|
||||
typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env);
|
||||
|
||||
virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0;
|
||||
virtual bool __stdcall FunctionExists(const char* name) = 0;
|
||||
virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0;
|
||||
|
||||
virtual AVSValue __stdcall GetVar(const char* name) = 0;
|
||||
virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0;
|
||||
virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0;
|
||||
|
||||
virtual void __stdcall PushContext(int level=0) = 0;
|
||||
virtual void __stdcall PopContext() = 0;
|
||||
|
||||
// align should be 4 or 8
|
||||
virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0;
|
||||
|
||||
virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0;
|
||||
|
||||
virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0;
|
||||
|
||||
typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env);
|
||||
virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0;
|
||||
|
||||
virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
|
||||
|
||||
virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
|
||||
|
||||
virtual int __stdcall SetMemoryMax(int mem) = 0;
|
||||
|
||||
virtual int __stdcall SetWorkingDir(const char * newdir) = 0;
|
||||
|
||||
virtual void* __stdcall ManageCache(int key, void* data) = 0;
|
||||
|
||||
enum PlanarChromaAlignmentMode {
|
||||
PlanarChromaAlignmentOff,
|
||||
PlanarChromaAlignmentOn,
|
||||
PlanarChromaAlignmentTest };
|
||||
|
||||
virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0;
|
||||
|
||||
virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0;
|
||||
};
|
||||
|
||||
|
||||
// avisynth.dll exports this; it's a way to use it as a library, without
|
||||
// writing an AVS script or without going through AVIFile.
|
||||
IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif //__AVISYNTH_H__
|
|
@ -1,368 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffaudiosource.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
AudioBase::AudioBase() {
|
||||
DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE * 10];
|
||||
};
|
||||
|
||||
AudioBase::~AudioBase() {
|
||||
delete[] DecodingBuffer;
|
||||
};
|
||||
|
||||
size_t AudioBase::FindClosestAudioKeyFrame(int64_t Sample) {
|
||||
for (size_t i = 0; i < Frames.size(); i++) {
|
||||
if (Frames[i].SampleStart == Sample && Frames[i].KeyFrame)
|
||||
return i;
|
||||
else if (Frames[i].SampleStart > Sample && Frames[i].KeyFrame)
|
||||
return i - 1;
|
||||
}
|
||||
return Frames.size() - 1;
|
||||
}
|
||||
|
||||
void FFAudioSource::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
av_close_input_file(FormatContext);
|
||||
}
|
||||
|
||||
FFAudioSource::FFAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
|
||||
FormatContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
AudioTrack = Track;
|
||||
Frames = (*TrackIndices)[AudioTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (av_find_stream_info(FormatContext) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CodecContext = FormatContext->streams[AudioTrack]->codec;
|
||||
|
||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
uint8_t DummyBuf[512];
|
||||
if (GetAudio(DummyBuf, 0, 1, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
AP.BitsPerSample = av_get_bits_per_sample_format(CodecContext->sample_fmt);
|
||||
AP.Channels = CodecContext->channels;;
|
||||
AP.Float = AudioFMTIsFloat(CodecContext->sample_fmt);
|
||||
AP.SampleRate = CodecContext->sample_rate;
|
||||
AP.NumSamples = (Frames.back()).SampleStart;
|
||||
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
||||
Free(true);
|
||||
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
int FFAudioSource::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize) {
|
||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
AVPacket Packet;
|
||||
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
if (Packet.stream_index == AudioTrack) {
|
||||
uint8_t *Data = Packet.data;
|
||||
int Size = Packet.size;
|
||||
|
||||
while (Size > 0) {
|
||||
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * 10;
|
||||
Ret = avcodec_decode_audio2(CodecContext, (int16_t *)Buf, &TempOutputBufSize, Data, Size);
|
||||
|
||||
if (Ret < 0) {// throw error or something?
|
||||
av_free_packet(&Packet);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (Ret > 0) {
|
||||
Size -= Ret;
|
||||
Data += Ret;
|
||||
Buf += TempOutputBufSize;
|
||||
if (SizeConst)
|
||||
*Count += TempOutputBufSize / SizeConst;
|
||||
}
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
}
|
||||
|
||||
Done:
|
||||
return Ret;
|
||||
}
|
||||
|
||||
int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||
size_t CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(Start) - 50, (int64_t)0);
|
||||
memset(Buf, 0, SizeConst * Count);
|
||||
AVPacket Packet;
|
||||
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
av_seek_frame(FormatContext, AudioTrack, Frames[CurrentAudioBlock].DTS, AVSEEK_FLAG_BACKWARD);
|
||||
|
||||
// Establish where we actually are
|
||||
// Trigger on packet dts difference since groups can otherwise be indistinguishable
|
||||
int64_t LastDTS = - 1;
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
if (Packet.stream_index == AudioTrack) {
|
||||
if (LastDTS < 0) {
|
||||
LastDTS = Packet.dts;
|
||||
} else if (LastDTS != Packet.dts) {
|
||||
for (size_t i = 0; i < Frames.size(); i++)
|
||||
if (Frames[i].DTS == Packet.dts) {
|
||||
// The current match was consumed
|
||||
CurrentAudioBlock = i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
}
|
||||
|
||||
uint8_t *DstBuf = (uint8_t *)Buf;
|
||||
int64_t RemainingSamples = Count;
|
||||
int64_t DecodeCount;
|
||||
|
||||
do {
|
||||
int64_t DecodeStart = Frames[CurrentAudioBlock].SampleStart;
|
||||
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//Env->ThrowError("Bleh, bad audio decoding");
|
||||
}
|
||||
CurrentAudioBlock++;
|
||||
|
||||
int64_t OffsetBytes = SizeConst * FFMAX(0, Start - DecodeStart);
|
||||
int64_t CopyBytes = FFMAX(0, SizeConst * FFMIN(RemainingSamples, DecodeCount - FFMAX(0, Start - DecodeStart)));
|
||||
|
||||
memcpy(DstBuf, DecodingBuffer + OffsetBytes, CopyBytes);
|
||||
DstBuf += CopyBytes;
|
||||
|
||||
if (SizeConst)
|
||||
RemainingSamples -= CopyBytes / SizeConst;
|
||||
|
||||
} while (RemainingSamples > 0 && CurrentAudioBlock < Frames.size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FFAudioSource::~FFAudioSource() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
void MatroskaAudioSource::Free(bool CloseCodec) {
|
||||
if (CS)
|
||||
cs_Destroy(CS);
|
||||
if (MC.ST.fp) {
|
||||
mkv_Close(MF);
|
||||
fclose(MC.ST.fp);
|
||||
}
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
av_free(CodecContext);
|
||||
}
|
||||
|
||||
MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
|
||||
CodecContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
TrackInfo *TI = NULL;
|
||||
CS = NULL;
|
||||
Frames = (*TrackIndices)[Track];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
MC.ST.fp = fopen(SourceFile, "rb");
|
||||
if (MC.ST.fp == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
mkv_SetTrackMask(MF, ~(1 << Track));
|
||||
TI = mkv_GetTrackInfo(MF, Track);
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (CS == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
CodecContext = avcodec_alloc_context();
|
||||
CodecContext->extradata = (uint8_t *)TI->CodecPrivate;
|
||||
CodecContext->extradata_size = TI->CodecPrivateSize;
|
||||
|
||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
uint8_t DummyBuf[512];
|
||||
if (GetAudio(DummyBuf, 0, 1, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
AP.BitsPerSample = av_get_bits_per_sample_format(CodecContext->sample_fmt);
|
||||
AP.Channels = CodecContext->channels;;
|
||||
AP.Float = AudioFMTIsFloat(CodecContext->sample_fmt);
|
||||
AP.SampleRate = CodecContext->sample_rate;
|
||||
AP.NumSamples = (Frames.back()).SampleStart;
|
||||
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
||||
Free(true);
|
||||
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
MatroskaAudioSource::~MatroskaAudioSource() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||
size_t CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(Start) - 10, (int64_t)0);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
|
||||
memset(Buf, 0, SizeConst * Count);
|
||||
|
||||
uint8_t *DstBuf = (uint8_t *)Buf;
|
||||
int64_t RemainingSamples = Count;
|
||||
int64_t DecodeCount;
|
||||
|
||||
do {
|
||||
int64_t DecodeStart = Frames[CurrentAudioBlock].SampleStart;
|
||||
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, Frames[CurrentAudioBlock].FilePos, Frames[CurrentAudioBlock].FrameSize, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//Env->ThrowError("Bleh, bad audio decoding");
|
||||
}
|
||||
CurrentAudioBlock++;
|
||||
|
||||
int64_t OffsetBytes = SizeConst * FFMAX(0, Start - DecodeStart);
|
||||
int64_t CopyBytes = FFMAX(0, SizeConst * FFMIN(RemainingSamples, DecodeCount - FFMAX(0, Start - DecodeStart)));
|
||||
|
||||
memcpy(DstBuf, DecodingBuffer + OffsetBytes, CopyBytes);
|
||||
DstBuf += CopyBytes;
|
||||
|
||||
if (SizeConst)
|
||||
RemainingSamples -= CopyBytes / SizeConst;
|
||||
|
||||
} while (RemainingSamples > 0 && CurrentAudioBlock < Frames.size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MatroskaAudioSource::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize) {
|
||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
|
||||
// FIXME check return
|
||||
ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize);
|
||||
int Size = FrameSize;
|
||||
uint8_t *Data = MC.Buffer;
|
||||
|
||||
while (Size > 0) {
|
||||
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
|
||||
Ret = avcodec_decode_audio2(CodecContext, (int16_t *)Buf, &TempOutputBufSize, Data, Size);
|
||||
|
||||
if (Ret < 0) // throw error or something?
|
||||
goto Done;
|
||||
|
||||
if (Ret > 0) {
|
||||
Size -= Ret;
|
||||
Data += Ret;
|
||||
Buf += TempOutputBufSize;
|
||||
if (SizeConst)
|
||||
*Count += TempOutputBufSize / SizeConst;
|
||||
}
|
||||
}
|
||||
|
||||
Done:
|
||||
return Ret;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 FFAUDIOSOURCE_H
|
||||
#define FFAUDIOSOURCE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include "indexing.h"
|
||||
#include "utils.h"
|
||||
#include "ffms.h"
|
||||
|
||||
class AudioBase {
|
||||
protected:
|
||||
uint8_t *DecodingBuffer;
|
||||
FrameInfoVector Frames;
|
||||
AVCodecContext *CodecContext;
|
||||
AudioProperties AP;
|
||||
|
||||
size_t FindClosestAudioKeyFrame(int64_t Sample);
|
||||
public:
|
||||
AudioBase();
|
||||
~AudioBase();
|
||||
|
||||
const AudioProperties& GetAudioProperties() { return AP; }
|
||||
virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
};
|
||||
|
||||
class FFAudioSource : public AudioBase {
|
||||
private:
|
||||
AVFormatContext *FormatContext;
|
||||
int AudioTrack;
|
||||
|
||||
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||
void Free(bool CloseCodec);
|
||||
public:
|
||||
FFAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFAudioSource();
|
||||
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
class MatroskaAudioSource : public AudioBase {
|
||||
private:
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext MC;
|
||||
CompressedStream *CS;
|
||||
char ErrorMessage[256];
|
||||
|
||||
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize);
|
||||
void Free(bool CloseCodec);
|
||||
public:
|
||||
MatroskaAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
|
||||
~MatroskaAudioSource();
|
||||
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,190 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffavisynth.h"
|
||||
#include "utils.h"
|
||||
|
||||
AvisynthVideoSource::AvisynthVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, int FPSNum, int FPSDen, const char *PP, int Threads, int SeekMode, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize) {
|
||||
memset(&VI, 0, sizeof(VI));
|
||||
SWS = NULL;
|
||||
ConvertToFormat = PIX_FMT_NONE;
|
||||
this->FPSNum = FPSNum;
|
||||
this->FPSDen = FPSDen;
|
||||
|
||||
VS = FFMS_CreateVideoSource(SourceFile, Track, TrackIndices, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
||||
if (!VS)
|
||||
Env->ThrowError(ErrorMsg);
|
||||
|
||||
const VideoProperties VP = *FFMS_GetVideoProperties(VS);
|
||||
|
||||
VI.image_type = VideoInfo::IT_TFF;
|
||||
VI.width = VP.Width;
|
||||
VI.height = VP.Height;
|
||||
|
||||
if (FPSNum > 0 && FPSDen > 0) {
|
||||
VI.fps_denominator = FPSDen;
|
||||
VI.fps_numerator = FPSNum;
|
||||
VI.num_frames = ceil(((VP.LastTime - VP.FirstTime) * FPSNum) / FPSDen);
|
||||
} else {
|
||||
VI.fps_denominator = VP.FPSDenominator;
|
||||
VI.fps_numerator = VP.FPSNumerator;
|
||||
VI.num_frames = VP.NumFrames;
|
||||
}
|
||||
|
||||
try {
|
||||
InitOutputFormat(VP.PixelFormat, Env);
|
||||
} catch (AvisynthError &) {
|
||||
FFMS_DestroyVideoSource(VS);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Set AR variables
|
||||
Env->SetVar("FFSAR_NUM", VP.SARNum);
|
||||
Env->SetVar("FFSAR_DEN", VP.SARDen);
|
||||
Env->SetVar("FFSAR", VP.SARNum / (double)VP.SARDen);
|
||||
|
||||
// Set crop variables
|
||||
Env->SetVar("FFCROP_LEFT", VP.CropLeft);
|
||||
Env->SetVar("FFCROP_RIGHT", VP.CropRight);
|
||||
Env->SetVar("FFCROP_TOP", VP.CropTop);
|
||||
Env->SetVar("FFCROP_BOTTOM", VP.CropBottom);
|
||||
}
|
||||
|
||||
AvisynthVideoSource::~AvisynthVideoSource() {
|
||||
if (SWS)
|
||||
sws_freeContext(SWS);
|
||||
FFMS_DestroyVideoSource(VS);
|
||||
}
|
||||
|
||||
void AvisynthVideoSource::InitOutputFormat(int CurrentFormat, IScriptEnvironment *Env) {
|
||||
int Loss;
|
||||
int BestFormat = avcodec_find_best_pix_fmt((1 << PIX_FMT_YUVJ420P) | (1 << PIX_FMT_YUV420P) | (1 << PIX_FMT_YUYV422) | (1 << PIX_FMT_RGB32) | (1 << PIX_FMT_BGR24), CurrentFormat, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
|
||||
|
||||
switch (BestFormat) {
|
||||
case PIX_FMT_YUVJ420P: // stupid yv12 distinctions, also inexplicably completely undeniably incompatible with all other supported output formats
|
||||
case PIX_FMT_YUV420P: VI.pixel_type = VideoInfo::CS_I420; break;
|
||||
case PIX_FMT_YUYV422: VI.pixel_type = VideoInfo::CS_YUY2; break;
|
||||
case PIX_FMT_RGB32: VI.pixel_type = VideoInfo::CS_BGR32; break;
|
||||
case PIX_FMT_BGR24: VI.pixel_type = VideoInfo::CS_BGR24; break;
|
||||
default:
|
||||
Env->ThrowError("FFVideoSource: No suitable output format found");
|
||||
}
|
||||
|
||||
if (BestFormat != CurrentFormat) {
|
||||
ConvertToFormat = BestFormat;
|
||||
SWS = sws_getContext(VI.width, VI.height, CurrentFormat, VI.width, VI.height, ConvertToFormat, GetCPUFlags() | SWS_BICUBIC, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (BestFormat == PIX_FMT_YUVJ420P || BestFormat == PIX_FMT_YUV420P) {
|
||||
VI.height -= VI.height & 1;
|
||||
VI.width -= VI.width & 1;
|
||||
}
|
||||
|
||||
if (BestFormat == PIX_FMT_YUYV422) {
|
||||
VI.width -= VI.width & 1;
|
||||
}
|
||||
}
|
||||
|
||||
PVideoFrame AvisynthVideoSource::OutputFrame(const AVFrameLite *Frame, IScriptEnvironment *Env) {
|
||||
// Yes, this function is overly complex and could probably be simplified
|
||||
AVPicture *SrcPicture = reinterpret_cast<AVPicture *>(const_cast<AVFrameLite *>(Frame));
|
||||
PVideoFrame Dst = Env->NewVideoFrame(VI);
|
||||
|
||||
if (ConvertToFormat != PIX_FMT_NONE && VI.pixel_type == VideoInfo::CS_I420) {
|
||||
uint8_t *DstData[3] = {Dst->GetWritePtr(PLANAR_Y), Dst->GetWritePtr(PLANAR_U), Dst->GetWritePtr(PLANAR_V)};
|
||||
int DstStride[3] = {Dst->GetPitch(PLANAR_Y), Dst->GetPitch(PLANAR_U), Dst->GetPitch(PLANAR_V)};
|
||||
sws_scale(SWS, SrcPicture->data, SrcPicture->linesize, 0, VI.height, DstData, DstStride);
|
||||
} else if (ConvertToFormat != PIX_FMT_NONE) {
|
||||
if (VI.IsRGB()) {
|
||||
uint8_t *DstData[1] = {Dst->GetWritePtr() + Dst->GetPitch() * (Dst->GetHeight() - 1)};
|
||||
int DstStride[1] = {-Dst->GetPitch()};
|
||||
sws_scale(SWS, SrcPicture->data, SrcPicture->linesize, 0, VI.height, DstData, DstStride);
|
||||
} else {
|
||||
uint8_t *DstData[1] = {Dst->GetWritePtr()};
|
||||
int DstStride[1] = {Dst->GetPitch()};
|
||||
sws_scale(SWS, SrcPicture->data, SrcPicture->linesize, 0, VI.height, DstData, DstStride);
|
||||
}
|
||||
} else if (VI.pixel_type == VideoInfo::CS_I420) {
|
||||
Env->BitBlt(Dst->GetWritePtr(PLANAR_Y), Dst->GetPitch(PLANAR_Y), SrcPicture->data[0], SrcPicture->linesize[0], Dst->GetRowSize(PLANAR_Y), Dst->GetHeight(PLANAR_Y));
|
||||
Env->BitBlt(Dst->GetWritePtr(PLANAR_U), Dst->GetPitch(PLANAR_U), SrcPicture->data[1], SrcPicture->linesize[1], Dst->GetRowSize(PLANAR_U), Dst->GetHeight(PLANAR_U));
|
||||
Env->BitBlt(Dst->GetWritePtr(PLANAR_V), Dst->GetPitch(PLANAR_V), SrcPicture->data[2], SrcPicture->linesize[2], Dst->GetRowSize(PLANAR_V), Dst->GetHeight(PLANAR_V));
|
||||
} else {
|
||||
if (VI.IsRGB())
|
||||
Env->BitBlt(Dst->GetWritePtr() + Dst->GetPitch() * (Dst->GetHeight() - 1), -Dst->GetPitch(), SrcPicture->data[0], SrcPicture->linesize[0], Dst->GetRowSize(), Dst->GetHeight());
|
||||
else
|
||||
Env->BitBlt(Dst->GetWritePtr(), Dst->GetPitch(), SrcPicture->data[0], SrcPicture->linesize[0], Dst->GetRowSize(), Dst->GetHeight());
|
||||
}
|
||||
|
||||
return Dst;
|
||||
}
|
||||
|
||||
PVideoFrame AvisynthVideoSource::GetFrame(int n, IScriptEnvironment *Env) {
|
||||
char ErrorMsg[1024];
|
||||
unsigned MsgSize = sizeof(ErrorMsg);
|
||||
const AVFrameLite *Frame;
|
||||
|
||||
if (FPSNum > 0 && FPSDen > 0)
|
||||
Frame = FFMS_GetFrameByTime(VS, FFMS_GetVideoProperties(VS)->FirstTime + (double)(n * (int64_t)FPSDen) / FPSNum, ErrorMsg, MsgSize);
|
||||
else
|
||||
Frame = FFMS_GetFrame(VS, n, ErrorMsg, MsgSize);
|
||||
|
||||
if (Frame == NULL)
|
||||
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
|
||||
|
||||
Env->SetVar("FFPICT_TYPE", Frame->PictType);
|
||||
return OutputFrame(Frame, Env);
|
||||
}
|
||||
|
||||
AvisynthAudioSource::AvisynthAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize) {
|
||||
memset(&VI, 0, sizeof(VI));
|
||||
|
||||
AS = FFMS_CreateAudioSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize);
|
||||
if (!AS)
|
||||
Env->ThrowError(ErrorMsg);
|
||||
|
||||
const AudioProperties AP = *FFMS_GetAudioProperties(AS);
|
||||
|
||||
VI.nchannels = AP.Channels;
|
||||
VI.num_audio_samples = AP.NumSamples;
|
||||
VI.audio_samples_per_second = AP.SampleRate;
|
||||
|
||||
if (AP.Float && AP.BitsPerSample == 32) {
|
||||
VI.sample_type = SAMPLE_FLOAT;
|
||||
} else {
|
||||
switch (AP.BitsPerSample) {
|
||||
case 8: VI.sample_type = SAMPLE_INT8; break;
|
||||
case 16: VI.sample_type = SAMPLE_INT16; break;
|
||||
case 24: VI.sample_type = SAMPLE_INT24; break;
|
||||
case 32: VI.sample_type = SAMPLE_INT32; break;
|
||||
default: Env->ThrowError("FFAudioSource: Bad audio format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AvisynthAudioSource::~AvisynthAudioSource() {
|
||||
FFMS_DestroyAudioSource(AS);
|
||||
}
|
||||
|
||||
void AvisynthAudioSource::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env) {
|
||||
char ErrorMsg[1024];
|
||||
unsigned MsgSize = sizeof(ErrorMsg);
|
||||
if (FFMS_GetAudio(AS, Buf, Start, Count, ErrorMsg, MsgSize))
|
||||
Env->ThrowError(ErrorMsg);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 FFAVISYNTH_H
|
||||
#define FFAVISYNTH_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libpostproc/postprocess.h>
|
||||
}
|
||||
|
||||
#include <windows.h>
|
||||
#include "avisynth.h"
|
||||
#include "ffms.h"
|
||||
|
||||
class AvisynthVideoSource : public IClip {
|
||||
private:
|
||||
VideoInfo VI;
|
||||
VideoBase *VS;
|
||||
SwsContext *SWS;
|
||||
int ConvertToFormat;
|
||||
int FPSNum;
|
||||
int FPSDen;
|
||||
|
||||
void InitOutputFormat(int CurrentFormat, IScriptEnvironment *Env);
|
||||
PVideoFrame OutputFrame(const AVFrameLite *SrcPicture, IScriptEnvironment *Env);
|
||||
public:
|
||||
AvisynthVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, int FPSNum, int FPSDen, const char *PP, int Threads, int SeekMode, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize);
|
||||
~AvisynthVideoSource();
|
||||
bool __stdcall GetParity(int n) { return false; }
|
||||
void __stdcall SetCacheHints(int cachehints, int frame_range) { }
|
||||
const VideoInfo& __stdcall GetVideoInfo() { return VI; }
|
||||
void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env) { }
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env);
|
||||
};
|
||||
|
||||
class AvisynthAudioSource : public IClip {
|
||||
private:
|
||||
VideoInfo VI;
|
||||
AudioBase *AS;
|
||||
public:
|
||||
AvisynthAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize);
|
||||
~AvisynthAudioSource();
|
||||
bool __stdcall GetParity(int n) { return false; }
|
||||
void __stdcall SetCacheHints(int cachehints, int frame_range) { }
|
||||
const VideoInfo& __stdcall GetVideoInfo() { return VI; }
|
||||
void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env);
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env) { return NULL; };
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,236 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 <string>
|
||||
#include "ffms.h"
|
||||
#include "ffavisynth.h"
|
||||
#include "ffswscale.h"
|
||||
#include "ffpp.h"
|
||||
|
||||
int GetNumberOfLogicalCPUs() {
|
||||
SYSTEM_INFO SI;
|
||||
GetSystemInfo(&SI);
|
||||
return SI.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
AVSValue __cdecl CreateFFIndex(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
FFMS_Init();
|
||||
|
||||
char ErrorMsg[1024];
|
||||
unsigned MsgSize = sizeof(ErrorMsg);
|
||||
|
||||
|
||||
if (!Args[0].Defined())
|
||||
Env->ThrowError("FFIndex: No source specified");
|
||||
|
||||
const char *Source = Args[0].AsString();
|
||||
const char *CacheFile = Args[1].AsString("");
|
||||
int IndexMask = Args[2].AsInt(0);
|
||||
int DumpMask = Args[3].AsInt(0);
|
||||
const char *AudioFile = Args[4].AsString("");
|
||||
bool OverWrite = Args[5].AsBool(false);
|
||||
|
||||
std::string DefaultCache(Source);
|
||||
DefaultCache.append(".ffindex");
|
||||
if (!strcmp(CacheFile, ""))
|
||||
CacheFile = DefaultCache.c_str();
|
||||
|
||||
if (!strcmp(AudioFile, ""))
|
||||
AudioFile = Source;
|
||||
|
||||
// Return values
|
||||
// 0: Index already present
|
||||
// 1: Index generated
|
||||
// 2: Index forced to be overwritten
|
||||
|
||||
FrameIndex *Index = NULL;
|
||||
if (OverWrite || !(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) {
|
||||
if (!(Index = FFMS_MakeIndex(Source, IndexMask, DumpMask, AudioFile, true, NULL, NULL, ErrorMsg, MsgSize)))
|
||||
Env->ThrowError("FFIndex: %s", ErrorMsg);
|
||||
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
Env->ThrowError("FFIndex: %s", ErrorMsg);
|
||||
}
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
if (!OverWrite)
|
||||
return AVSValue(1);
|
||||
else
|
||||
return AVSValue(2);
|
||||
} else {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return AVSValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
FFMS_Init();
|
||||
|
||||
char ErrorMsg[1024];
|
||||
unsigned MsgSize = sizeof(ErrorMsg);
|
||||
|
||||
if (!Args[0].Defined())
|
||||
Env->ThrowError("FFVideoSource: No source specified");
|
||||
|
||||
const char *Source = Args[0].AsString();
|
||||
int Track = Args[1].AsInt(-1);
|
||||
bool Cache = Args[2].AsBool(true);
|
||||
const char *CacheFile = Args[3].AsString("");
|
||||
int FPSNum = Args[4].AsInt(-1);
|
||||
int FPSDen = Args[5].AsInt(1);
|
||||
const char *PP = Args[6].AsString("");
|
||||
int Threads = Args[7].AsInt(-1);
|
||||
const char *Timecodes = Args[8].AsString("");
|
||||
int SeekMode = Args[9].AsInt(1);
|
||||
|
||||
if (Track <= -2)
|
||||
Env->ThrowError("FFVideoSource: No video track selected");
|
||||
|
||||
if (SeekMode < -1 || SeekMode > 3)
|
||||
Env->ThrowError("FFVideoSource: Invalid seekmode selected");
|
||||
|
||||
if (Threads <= 0)
|
||||
Threads = GetNumberOfLogicalCPUs();
|
||||
if (Threads < 1)
|
||||
Env->ThrowError("FFVideoSource: Invalid thread count");
|
||||
|
||||
if (!_stricmp(Source, Timecodes))
|
||||
Env->ThrowError("FFVideoSource: Timecodes will overwrite the source");
|
||||
|
||||
std::string DefaultCache(Source);
|
||||
DefaultCache.append(".ffindex");
|
||||
if (!strcmp(CacheFile, ""))
|
||||
CacheFile = DefaultCache.c_str();
|
||||
|
||||
FrameIndex *Index = NULL;
|
||||
if (Cache)
|
||||
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
|
||||
if (!Index) {
|
||||
if (!(Index = FFMS_MakeIndex(Source, 0, 0, NULL, true, NULL, NULL, ErrorMsg, MsgSize)))
|
||||
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
|
||||
|
||||
if (Cache)
|
||||
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (Track == -1)
|
||||
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_VIDEO, ErrorMsg, MsgSize);
|
||||
if (Track < 0)
|
||||
Env->ThrowError("FFVideoSource: No video track found");
|
||||
|
||||
if (strcmp(Timecodes, "")) {
|
||||
if (FFMS_WriteTimecodes(FFMS_GetTITrackIndex(Index, Track, ErrorMsg, MsgSize), Timecodes, ErrorMsg, MsgSize)) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
AvisynthVideoSource *Filter;
|
||||
|
||||
try {
|
||||
Filter = new AvisynthVideoSource(Source, Track, Index, FPSNum, FPSDen, PP, Threads, SeekMode, Env, ErrorMsg, MsgSize);
|
||||
} catch (...) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
throw;
|
||||
}
|
||||
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return Filter;
|
||||
}
|
||||
|
||||
AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
FFMS_Init();
|
||||
|
||||
char ErrorMsg[1024];
|
||||
unsigned MsgSize = sizeof(ErrorMsg);
|
||||
|
||||
if (!Args[0].Defined())
|
||||
Env->ThrowError("FFAudioSource: No source specified");
|
||||
|
||||
const char *Source = Args[0].AsString();
|
||||
int Track = Args[1].AsInt(-1);
|
||||
bool Cache = Args[2].AsBool(true);
|
||||
const char *CacheFile = Args[3].AsString("");
|
||||
|
||||
if (Track <= -2)
|
||||
Env->ThrowError("FFAudioSource: No audio track selected");
|
||||
|
||||
std::string DefaultCache(Source);
|
||||
DefaultCache.append(".ffindex");
|
||||
if (!strcmp(CacheFile, ""))
|
||||
CacheFile = DefaultCache.c_str();
|
||||
|
||||
FrameIndex *Index = NULL;
|
||||
if (Cache)
|
||||
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
|
||||
if (!Index) {
|
||||
if (!(Index = FFMS_MakeIndex(Source, -1, 0, CacheFile, true, NULL, NULL, ErrorMsg, MsgSize)))
|
||||
Env->ThrowError("FFAudioSource: %s", ErrorMsg);
|
||||
|
||||
if (Cache)
|
||||
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
Env->ThrowError("FFAudioSource: %s", ErrorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (Track == -1)
|
||||
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, ErrorMsg, MsgSize);
|
||||
if (Track < 0)
|
||||
Env->ThrowError("FFAudioSource: No audio track found");
|
||||
|
||||
AvisynthAudioSource *Filter;
|
||||
|
||||
try {
|
||||
Filter = new AvisynthAudioSource(Source, Track, Index, Env, ErrorMsg, MsgSize);
|
||||
} catch (...) {
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
throw;
|
||||
}
|
||||
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return Filter;
|
||||
}
|
||||
|
||||
AVSValue __cdecl CreateFFPP(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
return new FFPP(Args[0].AsClip(), Args[1].AsString(""), Env);
|
||||
}
|
||||
|
||||
AVSValue __cdecl CreateSWScale(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
return new SWScale(Args[0].AsClip(), Args[1].AsInt(0), Args[2].AsInt(0), Args[3].AsString("BICUBIC"), Args[4].AsString(""), Env);
|
||||
}
|
||||
|
||||
AVSValue __cdecl FFNoLog(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
|
||||
FFMS_NoLog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) {
|
||||
Env->AddFunction("FFIndex", "[source]s[cachefile]s[indexmask]i[dumpmask]i[audiofile]s[overwrite]b", CreateFFIndex, 0);
|
||||
Env->AddFunction("FFVideoSource", "[source]s[track]i[cache]b[cachefile]s[fpsnum]i[fpsden]i[pp]s[threads]i[timecodes]s[seekmode]i", CreateFFVideoSource, 0);
|
||||
Env->AddFunction("FFAudioSource", "[source]s[track]i[cache]b[cachefile]s", CreateFFAudioSource, 0);
|
||||
Env->AddFunction("FFPP", "c[pp]s", CreateFFPP, 0);
|
||||
Env->AddFunction("SWScale", "c[width]i[height]i[resizer]s[colorspace]s", CreateSWScale, 0);
|
||||
Env->AddFunction("FFNoLog", "", FFNoLog, 0);
|
||||
|
||||
return "FFmpegSource - The Second Coming";
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffms.h"
|
||||
#include "ffvideosource.h"
|
||||
#include "ffaudiosource.h"
|
||||
#include "indexing.h"
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
FrameInfo::FrameInfo(int64_t DTS, bool KeyFrame) {
|
||||
this->DTS = DTS;
|
||||
this->SampleStart = 0;
|
||||
this->FilePos = 0;
|
||||
this->FrameSize = 0;
|
||||
this->KeyFrame = KeyFrame;
|
||||
}
|
||||
|
||||
FrameInfo::FrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame) {
|
||||
this->DTS = DTS;
|
||||
this->SampleStart = SampleStart;
|
||||
this->FilePos = 0;
|
||||
this->FrameSize = 0;
|
||||
this->KeyFrame = KeyFrame;
|
||||
}
|
||||
|
||||
FrameInfo::FrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame) {
|
||||
this->DTS = 0;
|
||||
this->SampleStart = SampleStart;
|
||||
this->FilePos = FilePos;
|
||||
this->FrameSize = FrameSize;
|
||||
this->KeyFrame = KeyFrame;
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_Init() {
|
||||
static bool InitDone = false;
|
||||
if (!InitDone)
|
||||
av_register_all();
|
||||
InitDone = true;
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_NoLog() {
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
}
|
||||
|
||||
FFMS_API(VideoBase *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) {
|
||||
try {
|
||||
switch (TrackIndices->Decoder) {
|
||||
case 0: return new FFVideoSource(SourceFile, Track, TrackIndices, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
||||
case 1: return new MatroskaVideoSource(SourceFile, Track, TrackIndices, PP, Threads, ErrorMsg, MsgSize);
|
||||
#ifdef HAALITS
|
||||
case 2: return new HaaliTSVideoSource(SourceFile, Track, TrackIndices, PP, Threads, ErrorMsg, MsgSize);
|
||||
#endif
|
||||
default:
|
||||
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||
return NULL;
|
||||
}
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(AudioBase *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
|
||||
try {
|
||||
switch (TrackIndices->Decoder) {
|
||||
case 0: return new FFAudioSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize);
|
||||
case 1: return new MatroskaAudioSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize);
|
||||
default:
|
||||
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||
return NULL;
|
||||
}
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(VideoBase *VB) {
|
||||
delete VB;
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(AudioBase *AB) {
|
||||
delete AB;
|
||||
}
|
||||
|
||||
FFMS_API(const VideoProperties *) FFMS_GetVideoProperties(VideoBase *VB) {
|
||||
return &VB->GetVideoProperties();
|
||||
}
|
||||
|
||||
FFMS_API(const AudioProperties *) FFMS_GetAudioProperties(AudioBase *AB) {
|
||||
return &AB->GetAudioProperties();
|
||||
}
|
||||
|
||||
FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
return (AVFrameLite *)VB->GetFrame(n, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(const AVFrameLite *) FFMS_GetFrameByTime(VideoBase *VB, double Time, char *ErrorMsg, unsigned MsgSize) {
|
||||
return (AVFrameLite *)VB->GetFrameByTime(Time, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetAudio(AudioBase *AB, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
||||
return AB->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_SetOutputFormat(VideoBase *VB, int TargetFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize) {
|
||||
return VB->SetOutputFormat(TargetFormat, Width, Height, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_ResetOutputFormat(VideoBase *VB) {
|
||||
VB->ResetOutputFormat();
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_DestroyFrameIndex(FrameIndex *FI) {
|
||||
delete FI;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FrameIndex *TrackIndices, int TrackType, char *ErrorMsg, unsigned MsgSize) {
|
||||
for (int i = 0; i < TrackIndices->size(); i++)
|
||||
if ((*TrackIndices)[i].TT == TrackType)
|
||||
return i;
|
||||
_snprintf(ErrorMsg, MsgSize, "No suitable track found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetNumTracks(FrameIndex *TrackIndices) {
|
||||
return TrackIndices->size();
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetTrackType(FrameInfoVector *FIV) {
|
||||
return FIV->TT;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetNumFrames(FrameInfoVector *FIV) {
|
||||
return FIV->size();
|
||||
}
|
||||
|
||||
FFMS_API(const FrameInfo *) FFMS_GetFrameInfo(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (Frame < 0 || Frame >= FIV->size()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Invalid frame specified");
|
||||
return NULL;
|
||||
} else {
|
||||
return &(*FIV)[Frame];
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(FrameInfoVector *) FFMS_GetTITrackIndex(FrameIndex *TrackIndices, int Track, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (Track < 0 || Track >= TrackIndices->size()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Invalid track specified");
|
||||
return NULL;
|
||||
} else {
|
||||
return &(*TrackIndices)[Track];
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(FrameInfoVector *) FFMS_GetVSTrackIndex(VideoBase *VB) {
|
||||
return VB->GetFrameInfoVector();
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_FindClosestKeyFrame(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (Frame < 0 || Frame >= FIV->size()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Out of range frame specified");
|
||||
return -1;
|
||||
} else {
|
||||
return FIV->FindClosestKeyFrame(Frame);
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_FrameFromDTS(FrameInfoVector *FIV, int64_t DTS) {
|
||||
return FIV->FrameFromDTS(DTS);
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_ClosestFrameFromDTS(FrameInfoVector *FIV, int64_t DTS) {
|
||||
return FIV->ClosestFrameFromDTS(DTS);
|
||||
}
|
||||
|
||||
FFMS_API(const TrackTimeBase *) FFMS_GetTimeBase(FrameInfoVector *FIV) {
|
||||
return &FIV->TB;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FrameInfoVector *FIV, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
return FIV->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(FrameIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
|
||||
return MakeIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(FrameIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
return ReadIndex(IndexFile, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
|
||||
return WriteIndex(IndexFile, TrackIndices, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 FFMS_H
|
||||
#define FFMS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define FFMS_EXPORTS
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define EXTERN_C extern "C"
|
||||
#else
|
||||
# define EXTERN_C
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define FFMS_CC __stdcall
|
||||
# ifdef FFMS_EXPORTS
|
||||
# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC
|
||||
# else
|
||||
# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC
|
||||
# endif
|
||||
#else
|
||||
# define FFMS_CC
|
||||
# define FFMS_API(ret) EXTERN_C ret FFMS_CC
|
||||
#endif
|
||||
|
||||
class VideoBase;
|
||||
class AudioBase;
|
||||
class FrameIndex;
|
||||
class FrameInfoVector;
|
||||
|
||||
typedef int (FFMS_CC *IndexCallback)(int State, int64_t Current, int64_t Total, void *Private);
|
||||
|
||||
enum TrackType {
|
||||
FFMS_TYPE_VIDEO = 0,
|
||||
FFMS_TYPE_AUDIO = 1,
|
||||
};
|
||||
|
||||
// PixelFormat declarations from avutil.h so external libraries don't necessarily have to include ffmpeg headers
|
||||
enum FFMS_PixelFormat {
|
||||
FFMS_PIX_FMT_NONE= -1,
|
||||
FFMS_PIX_FMT_YUV420P, ///< Planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
|
||||
FFMS_PIX_FMT_YUYV422, ///< Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
|
||||
FFMS_PIX_FMT_RGB24, ///< Packed RGB 8:8:8, 24bpp, RGBRGB...
|
||||
FFMS_PIX_FMT_BGR24, ///< Packed RGB 8:8:8, 24bpp, BGRBGR...
|
||||
FFMS_PIX_FMT_YUV422P, ///< Planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
|
||||
FFMS_PIX_FMT_YUV444P, ///< Planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
|
||||
FFMS_PIX_FMT_RGB32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in cpu endianness
|
||||
FFMS_PIX_FMT_YUV410P, ///< Planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
|
||||
FFMS_PIX_FMT_YUV411P, ///< Planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
|
||||
FFMS_PIX_FMT_RGB565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), in cpu endianness
|
||||
FFMS_PIX_FMT_RGB555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in cpu endianness most significant bit to 0
|
||||
FFMS_PIX_FMT_GRAY8, ///< Y , 8bpp
|
||||
FFMS_PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black
|
||||
FFMS_PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white
|
||||
FFMS_PIX_FMT_PAL8, ///< 8 bit with PIX_FMT_RGB32 palette
|
||||
FFMS_PIX_FMT_YUVJ420P, ///< Planar YUV 4:2:0, 12bpp, full scale (jpeg)
|
||||
FFMS_PIX_FMT_YUVJ422P, ///< Planar YUV 4:2:2, 16bpp, full scale (jpeg)
|
||||
FFMS_PIX_FMT_YUVJ444P, ///< Planar YUV 4:4:4, 24bpp, full scale (jpeg)
|
||||
FFMS_PIX_FMT_XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing(xvmc_render.h)
|
||||
FFMS_PIX_FMT_XVMC_MPEG2_IDCT,
|
||||
FFMS_PIX_FMT_UYVY422, ///< Packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
|
||||
FFMS_PIX_FMT_UYYVYY411, ///< Packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3
|
||||
FFMS_PIX_FMT_BGR32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in cpu endianness
|
||||
FFMS_PIX_FMT_BGR565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), in cpu endianness
|
||||
FFMS_PIX_FMT_BGR555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in cpu endianness most significant bit to 1
|
||||
FFMS_PIX_FMT_BGR8, ///< Packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb)
|
||||
FFMS_PIX_FMT_BGR4, ///< Packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb)
|
||||
FFMS_PIX_FMT_BGR4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb)
|
||||
FFMS_PIX_FMT_RGB8, ///< Packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb)
|
||||
FFMS_PIX_FMT_RGB4, ///< Packed RGB 1:2:1, 4bpp, (msb)2R 3G 3B(lsb)
|
||||
FFMS_PIX_FMT_RGB4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)2R 3G 3B(lsb)
|
||||
FFMS_PIX_FMT_NV12, ///< Planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV
|
||||
FFMS_PIX_FMT_NV21, ///< as above, but U and V bytes are swapped
|
||||
|
||||
FFMS_PIX_FMT_RGB32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in cpu endianness
|
||||
FFMS_PIX_FMT_BGR32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in cpu endianness
|
||||
|
||||
FFMS_PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian
|
||||
FFMS_PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian
|
||||
FFMS_PIX_FMT_YUV440P, ///< Planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
|
||||
FFMS_PIX_FMT_YUVJ440P, ///< Planar YUV 4:4:0 full scale (jpeg)
|
||||
FFMS_PIX_FMT_YUVA420P, ///< Planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
|
||||
};
|
||||
|
||||
// This is a subset of the original AVFrame only containing the most used parts.
|
||||
// Even if it might seem like a good idea to cast it back to a full AVFrame to
|
||||
// access a few more values you really shouldn't do that. Only the values present
|
||||
// in AVFrameLite are actually updated when postprocessing is used.
|
||||
|
||||
struct AVFrameLite {
|
||||
uint8_t *Data[4];
|
||||
int Linesize[4];
|
||||
uint8_t *Base[4];
|
||||
int KeyFrame;
|
||||
int PictType;
|
||||
};
|
||||
|
||||
struct TrackTimeBase {
|
||||
int Num;
|
||||
int Den;
|
||||
};
|
||||
|
||||
class FrameInfo {
|
||||
public:
|
||||
int64_t DTS;
|
||||
int64_t SampleStart;
|
||||
int64_t FilePos;
|
||||
unsigned int FrameSize;
|
||||
bool KeyFrame;
|
||||
FrameInfo(int64_t DTS, bool KeyFrame);
|
||||
FrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame);
|
||||
FrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame);
|
||||
};
|
||||
|
||||
struct VideoProperties {
|
||||
int Width;
|
||||
int Height;
|
||||
int FPSDenominator;
|
||||
int FPSNumerator;
|
||||
int NumFrames;
|
||||
int PixelFormat;
|
||||
int SARNum;
|
||||
int SARDen;
|
||||
int CropTop;
|
||||
int CropBottom;
|
||||
int CropLeft;
|
||||
int CropRight;
|
||||
double FirstTime;
|
||||
double LastTime;
|
||||
};
|
||||
|
||||
struct AudioProperties {
|
||||
int SampleRate;
|
||||
int Channels;
|
||||
int BitsPerSample;
|
||||
bool Float;
|
||||
int64_t NumSamples;
|
||||
};
|
||||
|
||||
// Most functions return 0 on success
|
||||
// Functions without error message output can be assumed to never fail
|
||||
FFMS_API(void) FFMS_Init();
|
||||
FFMS_API(void) FFMS_NoLog();
|
||||
FFMS_API(VideoBase *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(AudioBase *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(VideoBase *VB);
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(AudioBase *AB);
|
||||
FFMS_API(const VideoProperties *) FFMS_GetVideoProperties(VideoBase *VB);
|
||||
FFMS_API(const AudioProperties *) FFMS_GetAudioProperties(AudioBase *AB);
|
||||
FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(const AVFrameLite *) FFMS_GetFrameByTime(VideoBase *VB, double Time, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetAudio(AudioBase *AB, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_SetOutputFormat(VideoBase *VB, int TargetFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(void) FFMS_ResetOutputFormat(VideoBase *VB);
|
||||
FFMS_API(void) FFMS_DestroyFrameIndex(FrameIndex *FI);
|
||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FrameIndex *TrackIndices, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetNumTracks(FrameIndex *TrackIndices);
|
||||
FFMS_API(int) FFMS_GetTrackType(FrameInfoVector *FIV);
|
||||
FFMS_API(int) FFMS_GetNumFrames(FrameInfoVector *FIV);
|
||||
FFMS_API(const FrameInfo *) FFMS_GetFrameInfo(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FrameInfoVector *) FFMS_GetTITrackIndex(FrameIndex *TrackIndices, int Track, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FrameInfoVector *) FFMS_GetVSTrackIndex(VideoBase *VB);
|
||||
FFMS_API(FrameInfoVector *) FFMS_GetASTrackIndex(AudioBase *AB);
|
||||
FFMS_API(int) FFMS_FindClosestKeyFrame(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_FrameFromDTS(FrameInfoVector *FIV, int64_t DTS);
|
||||
FFMS_API(int) FFMS_ClosestFrameFromDTS(FrameInfoVector *FIV, int64_t DTS);
|
||||
FFMS_API(const TrackTimeBase *) FFMS_GetTimeBase(FrameInfoVector *FIV);
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FrameInfoVector *FIV, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FrameIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FrameIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
|
||||
|
||||
#endif
|
|
@ -1,238 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>
|
||||
FFmpegSource2 Documentation
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>FFmpegSource2 Documentation</h1>
|
||||
<p>
|
||||
Opens files using ffmpeg and nothing else. May be frame accurate on good days. The source is MIT licensed and can be obtained from https://spaceboyz.net/svn/aegisub/trunk/FFmpegSource2. The precompiled binary is GPL licensed. If you are religious you may consider this the second coming.
|
||||
</p>
|
||||
|
||||
<h2>Compatibility - Video</h2>
|
||||
<ul>
|
||||
<li>AVI, MKV, MP4, FLV: Frame accurate</li>
|
||||
<li>WMV: Frame accurate(?) but avformat seems to pick keyframes relatively far away</li>
|
||||
<li>OGM: Messed up first frame and seeking produces smearing with seekmode=3, incredibly slow seeking without, remux to mkv or avi</li>
|
||||
<li>VOB: No rff flags applied, frame accurate?</li>
|
||||
<li>MPG: Seeking seems to be off by one or two frames now and then</li>
|
||||
<li>M2TS, TS: Linear access only (seekmode=-1)</li>
|
||||
<li>Image files: Most formats can be opened if seekmode=-1 is set</li>
|
||||
</ul>
|
||||
|
||||
<h2>Compatibility - Audio</h2>
|
||||
<ul>
|
||||
<li>All formats are written to separate wave64 files</li>
|
||||
<li>No wave64 reader is provided</li>
|
||||
</ul>
|
||||
|
||||
<h2>Usage</h2>
|
||||
<p>
|
||||
<b>FFIndex(string source, string cachefile = source + ".ffindex", int indexmask = 0, int dumpmask = 0, string audiofile = source, bool overwrite = false)</b><br />
|
||||
Used to invoke indexing separately and to write audio tracks to disk as wave64 files
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>FFVideoSource(string source, int track, bool cache = true, string cachefile = source + ".ffindex", int fpsnum = -1, int fpsden = 1, string pp, int threads = -1, string timecodes, int seekmode = 1)</b><br />
|
||||
Opens video, will invoke indexing with the defaults if no preexisting index is found
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>FFAudioSource(string source, int track, bool cache = true, string cachefile = source + ".ffindex")</b><br />
|
||||
Opens audio, <b>if an index already exists it needs to contain a suitable audio index or empty audio will be returned</b>, will invoke indexing with the defaults if no preexisting index is found
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>FFPP(clip, string pp)</b><br />
|
||||
Separate postprocessing which also seems to include a few simple deinterlacers
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>SWScale(clip, width = -1, height = -1, resizer = "BICUBIC", colorspace = "")</b><br />
|
||||
Separate postprocessing which also seems to include a few simple deinterlacers
|
||||
</p>
|
||||
|
||||
<b>FFNoLog()</b><br />
|
||||
Disable all logging output from FFmpeg
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<b>source:</b>
|
||||
Source file.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>indexmask & dumpmask:</b>
|
||||
Which audio tracks to index/write to disk. Dumping a track also implies indexing since the same work has to be done anyway. It is a binary mask meaning that 7 corresponds to writing tracks 1-3. Non-audio tracks are ignored. -1 writes all tracks.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>audiofile:</b>
|
||||
The base name to use for the decoded audio. Track number and delay is appended.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>overwrite:</b>
|
||||
Forces reindexing even if a valid index already exists. May be useful for trackmask changes or testing.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>track:</b>
|
||||
Track number as seen by the relevant demuxer, starts from 0, -1 means it will pick the first suitable track.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>fpsnum & fpsden:</b>
|
||||
For VFR -> CFR conversion. Setting fpsnum <= 0 means a 1:1 relation with the encoded frames.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>timecodes:</b>
|
||||
File to output timecodes to, if the file exists it will be overwritten.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>cache:</b>
|
||||
Write indexing information to a file for later use. This setting does not control if The video index is loaded which it always is if it exists.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>cachefile</b>
|
||||
Where to write the cache information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>pp:</b>
|
||||
See the table below for a full description, an empty string means no processing. It is recommended to avoid the autoq option since it's currently unknown what effect it will have on the processing.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>threads:</b>
|
||||
Sets the number of decoder threads used. Defaults to the number of cpus reported by windows. Ignored by lavc if the used decoder doesn't implement it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>seekmode:</b>
|
||||
Force how seeking is handled, has no effect on matroska files which always use the equivalent of seekmode=1<br />
|
||||
<b>-1:</b> linear access without rewind, will throw an error if each successive requested frame number isn't bigger than the last one, only intended for opening images but might work on well with some obscure video format<br />
|
||||
<b>0:</b> linear access, the definition of slow but should make some formats "usable"<br />
|
||||
<b>1:</b> safe normal, bases seeking decisions on the reported keyframe positions<br />
|
||||
<b>2:</b> unsafe normal, same as 1 but no error will be thrown if the exact destination has to be guessed<br />
|
||||
<b>3:</b> aggressive, seek in the forward direction even if no closer keyframe is known to exist, only useful for testing and containers where avformat doesn't report keyframes properly
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>width & height:</b>
|
||||
Width and height to resize to. Value below or equal to 0 is the same as specifying the input dimensions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>resizer:</b>
|
||||
Selects the resizer used for resampling the chroma planes and normal resizing. The available methods are: FAST_BILINEAR, BILINEAR, BICUBIC, X, POINT, AREA, BICUBLIN, GAUSS, SINC, LANCZOS and SPLINE.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>colorspace:</b>
|
||||
The colorspace to convert to. The names are YV12, YUY2, RGB24, RGB32 and the empty string for same as input.
|
||||
</p>
|
||||
|
||||
|
||||
<h2>PP string format</h2>
|
||||
<pre>
|
||||
Available postprocessing filters:
|
||||
Filters Options
|
||||
short long name short long option Description
|
||||
* * a autoq CPU power dependent enabler
|
||||
c chrom chrominance filtering enabled
|
||||
y nochrom chrominance filtering disabled
|
||||
n noluma luma filtering disabled
|
||||
hb hdeblock (2 threshold) horizontal deblocking filter
|
||||
1. difference factor: default=32, higher -> more deblocking
|
||||
2. flatness threshold: default=39, lower -> more deblocking
|
||||
the h & v deblocking filters share these
|
||||
so you can't set different thresholds for h / v
|
||||
vb vdeblock (2 threshold) vertical deblocking filter
|
||||
ha hadeblock (2 threshold) horizontal deblocking filter
|
||||
va vadeblock (2 threshold) vertical deblocking filter
|
||||
h1 x1hdeblock experimental h deblock filter 1
|
||||
v1 x1vdeblock experimental v deblock filter 1
|
||||
dr dering deringing filter
|
||||
al autolevels automatic brightness / contrast
|
||||
f fullyrange stretch luminance to (0..255)
|
||||
lb linblenddeint linear blend deinterlacer
|
||||
li linipoldeint linear interpolating deinterlace
|
||||
ci cubicipoldeint cubic interpolating deinterlacer
|
||||
md mediandeint median deinterlacer
|
||||
fd ffmpegdeint ffmpeg deinterlacer
|
||||
l5 lowpass5 FIR lowpass deinterlacer
|
||||
de default hb:a,vb:a,dr:a
|
||||
fa fast h1:a,v1:a,dr:a
|
||||
ac ha:a:128:7,va:a,dr:a
|
||||
tn tmpnoise (3 threshold) temporal noise reducer
|
||||
1. <= 2. <= 3. larger -> stronger filtering
|
||||
fq forceQuant <quantizer> force quantizer
|
||||
Usage:
|
||||
<filterName>[:<option>[:<option>...]][[,|/][-]<filterName>[:<option>...]]...
|
||||
long form example:
|
||||
vdeblock:autoq/hdeblock:autoq/linblenddeint default,-vdeblock
|
||||
short form example:
|
||||
vb:a/hb:a/lb de,-vb
|
||||
more examples:
|
||||
tn:64:128:256
|
||||
</pre>
|
||||
|
||||
<h2>Compiling</h2>
|
||||
|
||||
<p><b>zlib</b> from http://www.zlib.net/</p>
|
||||
|
||||
<p><b>FFmpeg svn</b> from http://ffmpeg.mplayerhq.hu/</p>
|
||||
|
||||
<p><b>Required FFmpeg Configuration:</b>
|
||||
./configure --enable-memalign-hack --enable-gpl --enable-swscale --enable-postproc
|
||||
|
||||
<p><b>Suggested Additional Options:</b>
|
||||
--enable-w32threads --disable-encoders --disable-muxers --disable-debug</p>
|
||||
|
||||
<p>
|
||||
Note that --enable-w32threads is required for multithreaded decoding to work.
|
||||
</p>
|
||||
|
||||
<h2>Changes</h2>
|
||||
<ul>
|
||||
<li>2.00 beta 4<ul>
|
||||
<li>Added the function FFNoLog which suppresses all messages from ffmpeg</li>
|
||||
<li>Experimental new TS parsing using Haali's splitter (with bugs)</li>
|
||||
<li>Everything is now compiled with VS2008 and GCC 4.3.2</li>
|
||||
<li>Updated FFmpeg to rev 16383 (no libfaad2 this time)</li>
|
||||
</ul></li>
|
||||
|
||||
<li>2.00 beta 3<ul>
|
||||
<li>Compiled with libfaad2 again (has anyone seen a single aac file lavc can open right now?)</li>
|
||||
<li>More API changes (and even more are likely to come)</li>
|
||||
<li>Several access violations and memory leaks on opening and indexing files fixed</li>
|
||||
<li>Added a VFR to CFR mode</li>
|
||||
<li>Readded FFAudioSource support for other containers (glitches still present now and then but no separate raw cache is required and possibly less buggy)</li>
|
||||
<li>Renamed the dll to FFMS2.dll, FFMS2 is now the official short name of the project</li>
|
||||
<li>Updated FFmpeg to rev 15522</li>
|
||||
</ul></li>
|
||||
|
||||
<li>2.00 beta 2<ul>
|
||||
<li>More API changes (and more are likely to come)</li>
|
||||
<li>Includes a simple CLI indexing application</li>
|
||||
<li>FFIndex now takes a few more arguments</li>
|
||||
<li>Readded FFAudioSource (only matroska supported for now)</li>
|
||||
<li>Updated FFmpeg to rev 15396</li>
|
||||
</ul></li>
|
||||
|
||||
<li>2.00 beta 1<ul>
|
||||
<li>Can now be used as a stand alone library for making indices and retrieving frames</li>
|
||||
<li>Rewrote most things</li>
|
||||
<li>Updated FFmpeg to rev 15301</li>
|
||||
</ul></li>
|
||||
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,201 +0,0 @@
|
|||
// Copyright (c) 2008 Karl Blomster
|
||||
//
|
||||
// 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 <iostream>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include "ffms.h"
|
||||
|
||||
int TrackMask;
|
||||
int DumpMask;
|
||||
bool Overwrite;
|
||||
bool IgnoreErrors;
|
||||
std::string InputFile;
|
||||
std::string CacheFile;
|
||||
std::string AudioFile;
|
||||
|
||||
FrameIndex *Index;
|
||||
|
||||
|
||||
void PrintUsage () {
|
||||
using namespace std;
|
||||
cout << "FFmpegSource2 indexing app" << endl
|
||||
<< "Usage: ffmsindex [options] inputfile [outputfile]" << endl
|
||||
<< "If no output filename is specified, inputfile.ffindex will be used." << endl << endl
|
||||
<< "Options:" << endl
|
||||
<< "-f Force overwriting of existing index file, if any (default: no)" << endl
|
||||
<< "-s Silently skip indexing of audio tracks that cannot be read (default: no)" << endl
|
||||
<< "-t N Set the audio indexing mask to N (-1 means index all tracks, 0 means index none, default: 0)" << endl
|
||||
<< "-d N Set the audio decoding mask to N (mask syntax same as -t, default: 0)" << endl
|
||||
<< "-a NAME Set the audio output base filename to NAME (default: input filename)";
|
||||
}
|
||||
|
||||
|
||||
void ParseCMDLine (int argc, char *argv[]) {
|
||||
if (argc <= 1) {
|
||||
PrintUsage();
|
||||
throw "";
|
||||
}
|
||||
|
||||
// defaults
|
||||
InputFile = "";
|
||||
CacheFile = "";
|
||||
AudioFile = "";
|
||||
TrackMask = 0;
|
||||
DumpMask = 0;
|
||||
Overwrite = false;
|
||||
IgnoreErrors = false;
|
||||
|
||||
// argv[0] = name of program
|
||||
int i = 1;
|
||||
|
||||
while (i < argc) {
|
||||
std::string Option = argv[i];
|
||||
std::string OptionArg = "";
|
||||
if (i+1 < argc)
|
||||
OptionArg = argv[i+1];
|
||||
|
||||
if (!Option.compare("-f")) {
|
||||
Overwrite = true;
|
||||
} else if (!Option.compare("-s")) {
|
||||
IgnoreErrors = true;
|
||||
} else if (!Option.compare("-t")) {
|
||||
TrackMask = atoi(OptionArg.c_str());
|
||||
i++;
|
||||
} else if (!Option.compare("-d")) {
|
||||
DumpMask = atoi(OptionArg.c_str());
|
||||
i++;
|
||||
} else if (!Option.compare("-a")) {
|
||||
AudioFile = OptionArg;
|
||||
i++;
|
||||
} else if (InputFile.empty()) {
|
||||
InputFile = argv[i];
|
||||
} else if (CacheFile.empty()) {
|
||||
CacheFile = argv[i];
|
||||
} else {
|
||||
std::cout << "Warning: ignoring unknown option " << argv[i] << std::endl;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (InputFile.empty()) {
|
||||
throw "Error: no input file specified";
|
||||
}
|
||||
if (CacheFile.empty()) {
|
||||
CacheFile = InputFile;
|
||||
CacheFile.append(".ffindex");
|
||||
}
|
||||
if (AudioFile.empty()) {
|
||||
AudioFile = InputFile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int FFMS_CC UpdateProgress(int State, int64_t Current, int64_t Total, void *Private) {
|
||||
using namespace std;
|
||||
int *LastPercentage = (int *)Private;
|
||||
int Percentage = int((double(Current)/double(Total)) * 100);
|
||||
|
||||
if (Percentage <= *LastPercentage)
|
||||
return 0;
|
||||
|
||||
*LastPercentage = Percentage;
|
||||
|
||||
/*if (Percentage < 10)
|
||||
cout << "\b\b";
|
||||
else
|
||||
cout << "\b\b\b"; */
|
||||
|
||||
cout << "Indexing, please wait... " << Percentage << "% \r";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DoIndexing () {
|
||||
char FFMSErrMsg[1024];
|
||||
int MsgSize = sizeof(FFMSErrMsg);
|
||||
int Progress = 0;
|
||||
|
||||
Index = FFMS_ReadIndex(CacheFile.c_str(), FFMSErrMsg, MsgSize);
|
||||
if (Overwrite || Index == NULL) {
|
||||
std::cout << "Indexing, please wait... 0% \r";
|
||||
Index = FFMS_MakeIndex(InputFile.c_str(), TrackMask, DumpMask, AudioFile.c_str(), IgnoreErrors, UpdateProgress, &Progress, FFMSErrMsg, MsgSize);
|
||||
if (Index == NULL) {
|
||||
std::string Err = "\nIndexing error: ";
|
||||
Err.append(FFMSErrMsg);
|
||||
throw Err;
|
||||
}
|
||||
|
||||
if (Progress != 100)
|
||||
std::cout << "Indexing, please wait... 100% \r";
|
||||
|
||||
std::cout << std::endl << "Writing index... ";
|
||||
|
||||
if (FFMS_WriteIndex(CacheFile.c_str(), Index, FFMSErrMsg, MsgSize)) {
|
||||
std::string Err = "Error writing index: ";
|
||||
Err.append(FFMSErrMsg);
|
||||
throw Err;
|
||||
}
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
} else {
|
||||
throw "Error: index file already exists, use -f if you are sure you want to overwrite it.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
ParseCMDLine(argc, argv);
|
||||
} catch (const char *Error) {
|
||||
std::cout << std::endl << Error << std::endl;
|
||||
return 1;
|
||||
} catch (std::string Error) {
|
||||
std::cout << std::endl << Error << std::endl;
|
||||
return 1;
|
||||
} catch (...) {
|
||||
std::cout << std::endl << "Unknown error" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FFMS_Init();
|
||||
|
||||
try {
|
||||
DoIndexing();
|
||||
} catch (const char *Error) {
|
||||
std::cout << Error << std::endl;
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return 1;
|
||||
} catch (std::string Error) {
|
||||
std::cout << std::endl << Error << std::endl;
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return 1;
|
||||
} catch (...) {
|
||||
std::cout << std::endl << "Unknown error" << std::endl;
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FFMS_DestroyFrameIndex(Index);
|
||||
return 0;
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffpp.h"
|
||||
#include "utils.h"
|
||||
|
||||
FFPP::FFPP(PClip AChild, const char *PP, IScriptEnvironment *Env) : GenericVideoFilter(AChild) {
|
||||
if (!strcmp(PP, ""))
|
||||
Env->ThrowError("FFPP: PP argument is empty");
|
||||
|
||||
PPContext = NULL;
|
||||
PPMode = NULL;
|
||||
SWSTo422P = NULL;
|
||||
SWSFrom422P = NULL;
|
||||
|
||||
memset(&InputPicture, 0, sizeof(InputPicture));
|
||||
memset(&OutputPicture, 0, sizeof(OutputPicture));
|
||||
|
||||
PPMode = pp_get_mode_by_name_and_quality((char *)PP, PP_QUALITY_MAX);
|
||||
if (!PPMode)
|
||||
Env->ThrowError("FFPP: Invalid postprocesing settings");
|
||||
|
||||
int Flags = GetCPUFlags();
|
||||
|
||||
if (vi.IsYV12()) {
|
||||
Flags |= PP_FORMAT_420;
|
||||
} else if (vi.IsYUY2()) {
|
||||
Flags |= PP_FORMAT_422;
|
||||
SWSTo422P = sws_getContext(vi.width, vi.height, PIX_FMT_YUV422, vi.width, vi.height, PIX_FMT_YUV422P, GetCPUFlags() | SWS_BICUBIC, NULL, NULL, NULL);
|
||||
SWSFrom422P = sws_getContext(vi.width, vi.height, PIX_FMT_YUV422P, vi.width, vi.height, PIX_FMT_YUV422, GetCPUFlags() | SWS_BICUBIC, NULL, NULL, NULL);
|
||||
avpicture_alloc(&InputPicture, PIX_FMT_YUV422P, vi.width, vi.height);
|
||||
avpicture_alloc(&OutputPicture, PIX_FMT_YUV422P, vi.width, vi.height);
|
||||
} else {
|
||||
Env->ThrowError("FFPP: Only YV12 and YUY2 video supported");
|
||||
}
|
||||
|
||||
PPContext = pp_get_context(vi.width, vi.height, Flags);
|
||||
if (!PPContext)
|
||||
Env->ThrowError("FFPP: Failed to create context");
|
||||
}
|
||||
|
||||
FFPP::~FFPP() {
|
||||
if (PPMode)
|
||||
pp_free_mode(PPMode);
|
||||
if (PPContext)
|
||||
pp_free_context(PPContext);
|
||||
if (SWSTo422P)
|
||||
sws_freeContext(SWSTo422P);
|
||||
if (SWSFrom422P)
|
||||
sws_freeContext(SWSFrom422P);
|
||||
avpicture_free(&InputPicture);
|
||||
avpicture_free(&OutputPicture);
|
||||
}
|
||||
|
||||
PVideoFrame FFPP::GetFrame(int n, IScriptEnvironment* Env) {
|
||||
PVideoFrame Src = child->GetFrame(n, Env);
|
||||
PVideoFrame Dst = Env->NewVideoFrame(vi);
|
||||
|
||||
if (vi.IsYV12()) {
|
||||
const uint8_t *SrcData[3] = {(uint8_t *)Src->GetReadPtr(PLANAR_Y), (uint8_t *)Src->GetReadPtr(PLANAR_U), (uint8_t *)Src->GetReadPtr(PLANAR_V)};
|
||||
int SrcStride[3] = {Src->GetPitch(PLANAR_Y), Src->GetPitch(PLANAR_U), Src->GetPitch(PLANAR_V)};
|
||||
uint8_t *DstData[3] = {Dst->GetWritePtr(PLANAR_Y), Dst->GetWritePtr(PLANAR_U), Dst->GetWritePtr(PLANAR_V)};
|
||||
int DstStride[3] = {Dst->GetPitch(PLANAR_Y), Dst->GetPitch(PLANAR_U), Dst->GetPitch(PLANAR_V)};
|
||||
|
||||
pp_postprocess(SrcData, SrcStride, DstData, DstStride, vi.width, vi.height, NULL, 0, PPMode, PPContext, 0);
|
||||
} else if (vi.IsYUY2()) {
|
||||
uint8_t *SrcData[1] = {(uint8_t *)Src->GetReadPtr()};
|
||||
int SrcStride[1] = {Src->GetPitch()};
|
||||
sws_scale(SWSTo422P, SrcData, SrcStride, 0, vi.height, InputPicture.data, InputPicture.linesize);
|
||||
|
||||
pp_postprocess(const_cast<const uint8_t **>(InputPicture.data), InputPicture.linesize, OutputPicture.data, OutputPicture.linesize, vi.width, vi.height, NULL, 0, PPMode, PPContext, 0);
|
||||
|
||||
uint8_t *DstData[1] = {Dst->GetWritePtr()};
|
||||
int DstStride[1] = {Dst->GetPitch()};
|
||||
sws_scale(SWSFrom422P, OutputPicture.data, OutputPicture.linesize, 0, vi.height, DstData, DstStride);
|
||||
}
|
||||
|
||||
return Dst;
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffswscale.h"
|
||||
#include "utils.h"
|
||||
|
||||
int CSNameToPIXFMT(const char * ACSName, int ADefault) {
|
||||
if (!_stricmp(ACSName, ""))
|
||||
return ADefault;
|
||||
if (!_stricmp(ACSName, "YV12"))
|
||||
return PIX_FMT_YUV420P;
|
||||
if (!_stricmp(ACSName, "YUY2"))
|
||||
return PIX_FMT_YUYV422;
|
||||
if (!_stricmp(ACSName, "RGB24"))
|
||||
return PIX_FMT_BGR24;
|
||||
if (!_stricmp(ACSName, "RGB32"))
|
||||
return PIX_FMT_RGB32;
|
||||
return PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
int ResizerNameToSWSResizer(const char *AResizerName) {
|
||||
if (!_stricmp(AResizerName, "FAST_BILINEAR"))
|
||||
return SWS_FAST_BILINEAR;
|
||||
if (!_stricmp(AResizerName, "BILINEAR"))
|
||||
return SWS_BILINEAR;
|
||||
if (!_stricmp(AResizerName, "BICUBIC"))
|
||||
return SWS_BICUBIC;
|
||||
if (!_stricmp(AResizerName, "X"))
|
||||
return SWS_X;
|
||||
if (!_stricmp(AResizerName, "POINT"))
|
||||
return SWS_POINT;
|
||||
if (!_stricmp(AResizerName, "AREA"))
|
||||
return SWS_AREA;
|
||||
if (!_stricmp(AResizerName, "BICUBLIN"))
|
||||
return SWS_BICUBLIN;
|
||||
if (!_stricmp(AResizerName, "GAUSS"))
|
||||
return SWS_GAUSS;
|
||||
if (!_stricmp(AResizerName, "SINC"))
|
||||
return SWS_SINC;
|
||||
if (!_stricmp(AResizerName, "LANCZOS"))
|
||||
return SWS_LANCZOS;
|
||||
if (!_stricmp(AResizerName, "SPLINE"))
|
||||
return SWS_SPLINE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWScale::SWScale(PClip Child, int ResizeToWidth, int ResizeToHeight, const char *ResizerName, const char *ConvertToFormatName, IScriptEnvironment *Env) : GenericVideoFilter(Child) {
|
||||
Context = NULL;
|
||||
OrigWidth = vi.width;
|
||||
OrigHeight = vi.height;
|
||||
FlipOutput = vi.IsYUV();
|
||||
|
||||
int ConvertFromFormat = PIX_FMT_NONE;
|
||||
if (vi.IsYV12())
|
||||
ConvertFromFormat = PIX_FMT_YUV420P;
|
||||
if (vi.IsYUY2())
|
||||
ConvertFromFormat = PIX_FMT_YUYV422;
|
||||
if (vi.IsRGB24())
|
||||
ConvertFromFormat = PIX_FMT_BGR24;
|
||||
if (vi.IsRGB32())
|
||||
ConvertFromFormat = PIX_FMT_RGB32;
|
||||
|
||||
if (ResizeToHeight <= 0)
|
||||
ResizeToHeight = OrigHeight;
|
||||
else
|
||||
vi.height = ResizeToHeight;
|
||||
|
||||
if (ResizeToWidth <= 0)
|
||||
ResizeToWidth = OrigWidth;
|
||||
else
|
||||
vi.width = ResizeToWidth;
|
||||
|
||||
int ConvertToFormat = CSNameToPIXFMT(ConvertToFormatName, ConvertFromFormat);
|
||||
if (ConvertToFormat == PIX_FMT_NONE)
|
||||
Env->ThrowError("SWScale: Invalid colorspace specified (%s)", ConvertToFormatName);
|
||||
|
||||
switch (ConvertToFormat) {
|
||||
case PIX_FMT_YUV420P: vi.pixel_type = VideoInfo::CS_I420; break;
|
||||
case PIX_FMT_YUYV422: vi.pixel_type = VideoInfo::CS_YUY2; break;
|
||||
case PIX_FMT_BGR24: vi.pixel_type = VideoInfo::CS_BGR24; break;
|
||||
case PIX_FMT_RGB32: vi.pixel_type = VideoInfo::CS_BGR32; break;
|
||||
}
|
||||
|
||||
FlipOutput ^= vi.IsYUV();
|
||||
|
||||
int Resizer = ResizerNameToSWSResizer(ResizerName);
|
||||
if (Resizer == 0)
|
||||
Env->ThrowError("SWScale: Invalid resizer specified (%s)", ResizerName);
|
||||
|
||||
if (ConvertToFormat == PIX_FMT_YUV420P && vi.height & 1)
|
||||
Env->ThrowError("SWScale: mod 2 output height required");
|
||||
|
||||
if ((ConvertToFormat == PIX_FMT_YUV420P || ConvertToFormat == PIX_FMT_YUYV422) && vi.width & 1)
|
||||
Env->ThrowError("SWScale: mod 2 output width required");
|
||||
|
||||
// may one day need a SWS_CS_DEFAULT in flags
|
||||
Context = sws_getContext(OrigWidth, OrigHeight, ConvertFromFormat, vi.width, vi.height, ConvertToFormat, GetCPUFlags() | Resizer, NULL, NULL, NULL);
|
||||
if (Context == NULL)
|
||||
Env->ThrowError("SWScale: Context creation failed");
|
||||
}
|
||||
|
||||
SWScale::~SWScale() {
|
||||
if (Context)
|
||||
sws_freeContext(Context);
|
||||
}
|
||||
|
||||
PVideoFrame SWScale::GetFrame(int n, IScriptEnvironment *Env) {
|
||||
PVideoFrame Src = child->GetFrame(n, Env);
|
||||
PVideoFrame Dst = Env->NewVideoFrame(vi);
|
||||
|
||||
uint8_t *SrcData[3] = {(uint8_t *)Src->GetReadPtr(PLANAR_Y), (uint8_t *)Src->GetReadPtr(PLANAR_U), (uint8_t *)Src->GetReadPtr(PLANAR_V)};
|
||||
int SrcStride[3] = {Src->GetPitch(PLANAR_Y), Src->GetPitch(PLANAR_U), Src->GetPitch(PLANAR_V)};
|
||||
|
||||
if (FlipOutput) {
|
||||
uint8_t *DstData[3] = {Dst->GetWritePtr(PLANAR_Y) + Dst->GetPitch(PLANAR_Y) * (Dst->GetHeight(PLANAR_Y) - 1), Dst->GetWritePtr(PLANAR_U) + Dst->GetPitch(PLANAR_U) * (Dst->GetHeight(PLANAR_U) - 1), Dst->GetWritePtr(PLANAR_V) + Dst->GetPitch(PLANAR_V) * (Dst->GetHeight(PLANAR_V) - 1)};
|
||||
int DstStride[3] = {-Dst->GetPitch(PLANAR_Y), -Dst->GetPitch(PLANAR_U), -Dst->GetPitch(PLANAR_V)};
|
||||
sws_scale(Context, SrcData, SrcStride, 0, OrigHeight, DstData, DstStride);
|
||||
} else {
|
||||
uint8_t *DstData[3] = {Dst->GetWritePtr(PLANAR_Y), Dst->GetWritePtr(PLANAR_U), Dst->GetWritePtr(PLANAR_V)};
|
||||
int DstStride[3] = {Dst->GetPitch(PLANAR_Y), Dst->GetPitch(PLANAR_U), Dst->GetPitch(PLANAR_V)};
|
||||
sws_scale(Context, SrcData, SrcStride, 0, OrigHeight, DstData, DstStride);
|
||||
}
|
||||
|
||||
return Dst;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 FFSWSCALE_H
|
||||
#define FFSWSCALE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include <windows.h>
|
||||
#include "avisynth.h"
|
||||
|
||||
class SWScale : public GenericVideoFilter {
|
||||
private:
|
||||
SwsContext *Context;
|
||||
int OrigWidth;
|
||||
int OrigHeight;
|
||||
bool FlipOutput;
|
||||
public:
|
||||
SWScale(PClip Child, int ResizeToWidth, int ResizeToHeight, const char *ResizerName, const char *ConvertToFormatName, IScriptEnvironment *Env);
|
||||
~SWScale();
|
||||
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,799 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "ffvideosource.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
int VideoBase::InitPP(const char *PP, int PixelFormat, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (PP == NULL || !strcmp(PP, ""))
|
||||
return 0;
|
||||
|
||||
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
|
||||
if (!PPMode) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Invalid postprocesing settings");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Flags = GetCPUFlags();
|
||||
|
||||
switch (PixelFormat) {
|
||||
case PIX_FMT_YUV420P: Flags |= PP_FORMAT_420; break;
|
||||
case PIX_FMT_YUV422P: Flags |= PP_FORMAT_422; break;
|
||||
case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break;
|
||||
case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break;
|
||||
default:
|
||||
_snprintf(ErrorMsg, MsgSize, "Input format is not supported for postprocessing");
|
||||
return 2;
|
||||
}
|
||||
|
||||
PPContext = pp_get_context(VP.Width, VP.Height, Flags);
|
||||
|
||||
if (!(PPFrame = avcodec_alloc_frame())) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to allocate temporary frame");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (avpicture_alloc((AVPicture *)PPFrame, PixelFormat, VP.Width, VP.Height) < 0) {
|
||||
av_free(PPFrame);
|
||||
PPFrame = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to allocate picture");
|
||||
return 4;
|
||||
}
|
||||
|
||||
FinalFrame = PPFrame;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFrameLite *VideoBase::OutputFrame(AVFrame *Frame) {
|
||||
if (PPContext) {
|
||||
pp_postprocess(const_cast<const uint8_t **>(Frame->data), Frame->linesize, PPFrame->data, PPFrame->linesize, VP.Width, VP.Height, Frame->qscale_table, Frame->qstride, PPMode, PPContext, Frame->pict_type | (Frame->qscale_type ? PP_PICT_TYPE_QP2 : 0));
|
||||
PPFrame->key_frame = Frame->key_frame;
|
||||
PPFrame->pict_type = Frame->pict_type;
|
||||
}
|
||||
|
||||
if (SWS) {
|
||||
sws_scale(SWS, PPFrame->data, PPFrame->linesize, 0, VP.Height, FinalFrame->data, FinalFrame->linesize);
|
||||
FinalFrame->key_frame = PPFrame->key_frame;
|
||||
FinalFrame->pict_type = PPFrame->pict_type;
|
||||
}
|
||||
|
||||
return reinterpret_cast<AVFrameLite *>(FinalFrame);
|
||||
}
|
||||
|
||||
VideoBase::VideoBase() {
|
||||
memset(&VP, 0, sizeof(VP));
|
||||
PPContext = NULL;
|
||||
PPMode = NULL;
|
||||
SWS = NULL;
|
||||
LastFrameNum = -1;
|
||||
CurrentFrame = 0;
|
||||
CodecContext = NULL;
|
||||
DecodeFrame = avcodec_alloc_frame();
|
||||
PPFrame = DecodeFrame;
|
||||
FinalFrame = PPFrame;
|
||||
}
|
||||
|
||||
VideoBase::~VideoBase() {
|
||||
if (PPMode)
|
||||
pp_free_mode(PPMode);
|
||||
if (PPContext)
|
||||
pp_free_context(PPContext);
|
||||
if (SWS)
|
||||
sws_freeContext(SWS);
|
||||
if (FinalFrame != PPFrame) {
|
||||
avpicture_free((AVPicture *)FinalFrame);
|
||||
av_free(FinalFrame);
|
||||
}
|
||||
if (PPFrame != DecodeFrame) {
|
||||
avpicture_free((AVPicture *)PPFrame);
|
||||
av_free(PPFrame);
|
||||
}
|
||||
av_free(DecodeFrame);
|
||||
}
|
||||
|
||||
AVFrameLite *VideoBase::GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize) {
|
||||
int Frame = Frames.ClosestFrameFromDTS((Time * 1000 * Frames.TB.Den) / Frames.TB.Num);
|
||||
return GetFrame(Frame, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
int VideoBase::SetOutputFormat(int TargetFormats, int Width, int Height, char *ErrorMsg, unsigned MsgSize) {
|
||||
// FIXME: investigate the possible bug in avcodec_find_best_pix_fmt
|
||||
// int Loss;
|
||||
// int OutputFormat = avcodec_find_best_pix_fmt(TargetFormats,
|
||||
// CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
|
||||
// if (OutputFormat == -1)
|
||||
// return -1;
|
||||
|
||||
int OutputFormat = TargetFormats;
|
||||
|
||||
SwsContext *NewSWS = NULL;
|
||||
if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) {
|
||||
NewSWS = sws_getContext(CodecContext->width, CodecContext->height, CodecContext->pix_fmt, Width, Height,
|
||||
OutputFormat, GetCPUFlags() | SWS_BICUBIC, NULL, NULL, NULL);
|
||||
if (NewSWS == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to allocate SWScale context");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (SWS)
|
||||
sws_freeContext(SWS);
|
||||
SWS = NewSWS;
|
||||
|
||||
VP.Height = Height;
|
||||
VP.Width = Width;
|
||||
VP.PixelFormat = OutputFormat;
|
||||
|
||||
// FIXME: In theory the allocations in this part could fail just like in InitPP but whatever
|
||||
if (FinalFrame != PPFrame) {
|
||||
avpicture_free((AVPicture *)FinalFrame);
|
||||
av_free(FinalFrame);
|
||||
}
|
||||
|
||||
if (SWS) {
|
||||
FinalFrame = avcodec_alloc_frame();
|
||||
avpicture_alloc((AVPicture *)FinalFrame, VP.PixelFormat, VP.Width, VP.Height);
|
||||
} else {
|
||||
FinalFrame = PPFrame;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VideoBase::ResetOutputFormat() {
|
||||
if (SWS)
|
||||
sws_freeContext(SWS);
|
||||
SWS = NULL;
|
||||
VP.Height = CodecContext->height;
|
||||
VP.Width = CodecContext->width;
|
||||
VP.PixelFormat = CodecContext->pix_fmt;
|
||||
}
|
||||
|
||||
void FFVideoSource::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
av_close_input_file(FormatContext);
|
||||
// how was it allocated? how was it deallocate? nobody knows
|
||||
//av_free(FormatContext);
|
||||
}
|
||||
|
||||
FFVideoSource::FFVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices,
|
||||
const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) {
|
||||
|
||||
FormatContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
this->SeekMode = SeekMode;
|
||||
VideoTrack = Track;
|
||||
Frames = (*TrackIndices)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (av_find_stream_info(FormatContext) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Video track is unseekable");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CodecContext = FormatContext->streams[VideoTrack]->codec;
|
||||
CodecContext->thread_count = Threads;
|
||||
|
||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
//VP.image_type = VideoInfo::IT_TFF;
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;
|
||||
VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num;
|
||||
VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den;
|
||||
VP.NumFrames = Frames.size();
|
||||
VP.PixelFormat = CodecContext->pix_fmt;
|
||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// sanity check framerate
|
||||
if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) {
|
||||
VP.FPSDenominator = 1;
|
||||
VP.FPSNumerator = 30;
|
||||
}
|
||||
|
||||
if (InitPP(PP, CodecContext->pix_fmt, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Adjust framerate to match the duration of the first frame
|
||||
if (Frames.size() >= 2) {
|
||||
unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1);
|
||||
VP.FPSDenominator *= DTSDiff;
|
||||
}
|
||||
|
||||
// Cannot "output" to PPFrame without doing all other initialization
|
||||
// This is the additional mess required for seekmode=-1 to work in a reasonable way
|
||||
OutputFrame(DecodeFrame);
|
||||
LastFrameNum = 0;
|
||||
|
||||
// Set AR variables
|
||||
VP.SARNum = CodecContext->sample_aspect_ratio.num;
|
||||
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
||||
}
|
||||
|
||||
FFVideoSource::~FFVideoSource() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int FFVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
AVPacket Packet;
|
||||
int FrameFinished = 0;
|
||||
*AStartTime = -1;
|
||||
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
if (Packet.stream_index == VideoTrack) {
|
||||
if (*AStartTime < 0)
|
||||
*AStartTime = Packet.dts;
|
||||
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, Packet.data, Packet.size);
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
|
||||
if (FrameFinished)
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames)
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, NULL, 0);
|
||||
|
||||
if (!FrameFinished)
|
||||
goto Error;
|
||||
|
||||
// Ignore errors for now
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFrameLite *FFVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame);
|
||||
|
||||
bool HasSeeked = false;
|
||||
|
||||
if (SeekMode >= 0) {
|
||||
int ClosestKF = Frames.FindClosestKeyFrame(n);
|
||||
|
||||
if (SeekMode == 0) {
|
||||
if (n < CurrentFrame) {
|
||||
av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
CurrentFrame = 0;
|
||||
}
|
||||
} else {
|
||||
// 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat
|
||||
if (n < CurrentFrame || ClosestKF > CurrentFrame + 10 || (SeekMode == 3 && n > CurrentFrame + 10)) {
|
||||
av_seek_frame(FormatContext, VideoTrack, (SeekMode == 3) ? Frames[n].DTS : Frames[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
HasSeeked = true;
|
||||
}
|
||||
}
|
||||
} else if (n < CurrentFrame) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Non-linear access attempted");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
if (HasSeeked) {
|
||||
HasSeeked = false;
|
||||
|
||||
// Is the seek destination time known? Does it belong to a frame?
|
||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
||||
switch (SeekMode) {
|
||||
case 1:
|
||||
_snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file");
|
||||
return NULL;
|
||||
case 2:
|
||||
case 3:
|
||||
CurrentFrame = Frames.ClosestFrameFromDTS(StartTime);
|
||||
break;
|
||||
default:
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed assertion");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentFrame++;
|
||||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
||||
|
||||
void MatroskaVideoSource::Free(bool CloseCodec) {
|
||||
if (CS)
|
||||
cs_Destroy(CS);
|
||||
if (MC.ST.fp) {
|
||||
mkv_Close(MF);
|
||||
fclose(MC.ST.fp);
|
||||
}
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
av_free(CodecContext);
|
||||
}
|
||||
|
||||
MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track,
|
||||
FrameIndex *TrackIndices, const char *PP,
|
||||
int Threads, char *ErrorMsg, unsigned MsgSize) {
|
||||
|
||||
AVCodec *Codec = NULL;
|
||||
CodecContext = NULL;
|
||||
TrackInfo *TI = NULL;
|
||||
CS = NULL;
|
||||
VideoTrack = Track;
|
||||
Frames = (*TrackIndices)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
MC.ST.fp = fopen(SourceFile, "rb");
|
||||
if (MC.ST.fp == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
|
||||
TI = mkv_GetTrackInfo(MF, VideoTrack);
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (CS == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
CodecContext = avcodec_alloc_context();
|
||||
CodecContext->extradata = (uint8_t *)TI->CodecPrivate;
|
||||
CodecContext->extradata_size = TI->CodecPrivateSize;
|
||||
CodecContext->thread_count = Threads;
|
||||
|
||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;;
|
||||
VP.FPSDenominator = 1;
|
||||
VP.FPSNumerator = 30;
|
||||
VP.NumFrames = Frames.size();
|
||||
VP.PixelFormat = CodecContext->pix_fmt;
|
||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (InitPP(PP, CodecContext->pix_fmt, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Calculate the average framerate
|
||||
if (Frames.size() >= 2) {
|
||||
double DTSDiff = (double)(Frames.back().DTS - Frames.front().DTS);
|
||||
VP.FPSDenominator = (unsigned int)(DTSDiff * mkv_TruncFloat(TI->TimecodeScale) / (double)1000 / (double)(VP.NumFrames - 1) + 0.5);
|
||||
VP.FPSNumerator = 1000000;
|
||||
}
|
||||
|
||||
// Output the already decoded frame so it isn't wasted
|
||||
OutputFrame(DecodeFrame);
|
||||
LastFrameNum = 0;
|
||||
|
||||
// Set AR variables
|
||||
VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
|
||||
VP.SARDen = TI->AV.Video.DisplayHeight * TI->AV.Video.PixelWidth;
|
||||
|
||||
// Set crop variables
|
||||
VP.CropLeft = TI->AV.Video.CropL;
|
||||
VP.CropRight = TI->AV.Video.CropR;
|
||||
VP.CropTop = TI->AV.Video.CropT;
|
||||
VP.CropBottom = TI->AV.Video.CropB;
|
||||
}
|
||||
|
||||
MatroskaVideoSource::~MatroskaVideoSource() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int MatroskaVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
int FrameFinished = 0;
|
||||
*AFirstStartTime = -1;
|
||||
|
||||
ulonglong StartTime, EndTime, FilePos;
|
||||
unsigned int Track, FrameFlags, FrameSize;
|
||||
|
||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||
if (*AFirstStartTime < 0)
|
||||
*AFirstStartTime = StartTime;
|
||||
|
||||
if (ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
||||
return 1;
|
||||
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, MC.Buffer, FrameSize);
|
||||
|
||||
if (FrameFinished)
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames)
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, NULL, 0);
|
||||
|
||||
if (!FrameFinished)
|
||||
goto Error;
|
||||
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFrameLite *MatroskaVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame);
|
||||
|
||||
bool HasSeeked = false;
|
||||
|
||||
if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame) {
|
||||
mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
HasSeeked = true;
|
||||
}
|
||||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
if (HasSeeked) {
|
||||
HasSeeked = false;
|
||||
|
||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentFrame++;
|
||||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
||||
|
||||
#ifdef HAALITS
|
||||
|
||||
void HaaliTSVideoSource::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
av_free(CodecContext);
|
||||
}
|
||||
|
||||
HaaliTSVideoSource::HaaliTSVideoSource(const char *SourceFile, int Track,
|
||||
FrameIndex *TrackIndices, const char *PP,
|
||||
int Threads, char *ErrorMsg, unsigned MsgSize) {
|
||||
|
||||
AVCodec *Codec = NULL;
|
||||
CodecContext = NULL;
|
||||
VideoTrack = Track;
|
||||
Frames = (*TrackIndices)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
CLSID clsid = Haali_TS_Parser;
|
||||
|
||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CComPtr<IMemAlloc> pMA;
|
||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CComPtr<IMMStream> pMS;
|
||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
WCHAR WSourceFile[2048];
|
||||
mbstowcs(WSourceFile, SourceFile, 2000);
|
||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open file");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
BSTR CodecID = NULL;
|
||||
uint8_t * CodecPrivate = NULL;
|
||||
int CodecPrivateSize = 0;
|
||||
int CurrentTrack = 0;
|
||||
CComPtr<IEnumUnknown> pEU;
|
||||
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
|
||||
CComPtr<IUnknown> pU;
|
||||
while (pEU->Next(1, &pU, NULL) == S_OK) {
|
||||
if (CurrentTrack++ == Track) {
|
||||
CComQIPtr<IPropertyBag> pBag = pU;
|
||||
if (pBag) {
|
||||
VARIANT pV;
|
||||
|
||||
pV.vt = VT_EMPTY;
|
||||
if (pBag->Read(L"CodecID", &pV, NULL) == S_OK)
|
||||
CodecID = pV.bstrVal;
|
||||
|
||||
pV.vt = VT_EMPTY;
|
||||
if (pBag->Read(L"CodecPrivate", &pV, NULL) == S_OK) {
|
||||
CodecPrivate = (uint8_t *)pV.parray->pvData;
|
||||
CodecPrivateSize = pV.parray->cbElements;
|
||||
}
|
||||
}
|
||||
}
|
||||
pU = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CodecContext = avcodec_alloc_context();
|
||||
CodecContext->extradata = CodecPrivate;
|
||||
CodecContext->extradata_size = CodecPrivateSize;
|
||||
CodecContext->thread_count = Threads;
|
||||
|
||||
char ACodecID[2048];
|
||||
wcstombs(ACodecID, CodecID, 2000);
|
||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, CodecPrivate));
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;;
|
||||
VP.FPSDenominator = 1;
|
||||
VP.FPSNumerator = 30;
|
||||
VP.NumFrames = Frames.size();
|
||||
VP.PixelFormat = CodecContext->pix_fmt;
|
||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (InitPP(PP, CodecContext->pix_fmt, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Calculate the average framerate
|
||||
if (Frames.size() >= 2) {
|
||||
double DTSDiff = (double)(Frames.back().DTS - Frames.front().DTS);
|
||||
// FIXME
|
||||
VP.FPSDenominator = (unsigned int)((DTSDiff * 1000000000) / (double)1000 / (double)(VP.NumFrames - 1) + 0.5);
|
||||
VP.FPSNumerator = 1000000;
|
||||
}
|
||||
|
||||
// Output the already decoded frame so it isn't wasted
|
||||
OutputFrame(DecodeFrame);
|
||||
LastFrameNum = 0;
|
||||
|
||||
// Set AR variables
|
||||
// VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
|
||||
// VP.SARDen = TI->AV.Video.DisplayHeight * TI->AV.Video.PixelWidth;
|
||||
|
||||
// Set crop variables
|
||||
// VP.CropLeft = TI->AV.Video.CropL;
|
||||
// VP.CropRight = TI->AV.Video.CropR;
|
||||
// VP.CropTop = TI->AV.Video.CropT;
|
||||
// VP.CropBottom = TI->AV.Video.CropB;
|
||||
}
|
||||
|
||||
HaaliTSVideoSource::~HaaliTSVideoSource() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int HaaliTSVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
int FrameFinished = 0;
|
||||
*AFirstStartTime = -1;
|
||||
|
||||
for (;;) {
|
||||
CComPtr<IMMFrame> pMMF;
|
||||
if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
|
||||
break;
|
||||
|
||||
REFERENCE_TIME Ts, Te;
|
||||
if (*AFirstStartTime < 0 && SUCCEEDED(pMMF->GetTime(&Ts, &Te)))
|
||||
*AFirstStartTime = Ts;
|
||||
|
||||
if (pMMF->GetTrack() == VideoTrack) {
|
||||
BYTE *Data = NULL;
|
||||
if (FAILED(pMMF->GetPointer(&Data)))
|
||||
goto Error;
|
||||
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, Data, pMMF->GetActualDataLength());
|
||||
|
||||
if (FrameFinished)
|
||||
goto Done;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames)
|
||||
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, NULL, 0);
|
||||
|
||||
if (!FrameFinished)
|
||||
goto Error;
|
||||
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFrameLite *HaaliTSVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame);
|
||||
|
||||
bool HasSeeked = false;
|
||||
|
||||
if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame) {
|
||||
int64_t dtsp = Frames[n].DTS;
|
||||
pMMC->Seek(Frames[n].DTS, MMSF_PREV_KF);
|
||||
// FIXME for some reason required to make it seek properly
|
||||
//avcodec_flush_buffers(CodecContext);
|
||||
HasSeeked = true;
|
||||
}
|
||||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
if (HasSeeked) {
|
||||
HasSeeked = false;
|
||||
|
||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentFrame++;
|
||||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
||||
|
||||
#endif // HAALITS
|
|
@ -1,120 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 FFVIDEOSOURCE_H
|
||||
#define FFVIDEOSOURCE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libpostproc/postprocess.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include "indexing.h"
|
||||
#include "utils.h"
|
||||
#include "ffms.h"
|
||||
|
||||
#ifdef HAALITS
|
||||
# define _WIN32_DCOM
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# include <atlbase.h>
|
||||
# include <dshow.h>
|
||||
# include "CoParser.h"
|
||||
# include <initguid.h>
|
||||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
class VideoBase {
|
||||
private:
|
||||
pp_context_t *PPContext;
|
||||
pp_mode_t *PPMode;
|
||||
SwsContext *SWS;
|
||||
protected:
|
||||
VideoProperties VP;
|
||||
AVFrame *DecodeFrame;
|
||||
AVFrame *PPFrame;
|
||||
AVFrame *FinalFrame;
|
||||
int LastFrameNum;
|
||||
FrameInfoVector Frames;
|
||||
int VideoTrack;
|
||||
int CurrentFrame;
|
||||
AVCodecContext *CodecContext;
|
||||
|
||||
VideoBase();
|
||||
int InitPP(const char *PP, int PixelFormat, char *ErrorMsg, unsigned MsgSize);
|
||||
AVFrameLite *OutputFrame(AVFrame *Frame);
|
||||
public:
|
||||
virtual ~VideoBase();
|
||||
const VideoProperties& GetVideoProperties() { return VP; }
|
||||
FrameInfoVector *GetFrameInfoVector() { return &Frames; }
|
||||
virtual AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
AVFrameLite *GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize);
|
||||
int SetOutputFormat(int TargetFormats, int Width, int Height, char *ErrorMsg, unsigned MsgSize);
|
||||
void ResetOutputFormat();
|
||||
};
|
||||
|
||||
class FFVideoSource : public VideoBase {
|
||||
private:
|
||||
AVFormatContext *FormatContext;
|
||||
int SeekMode;
|
||||
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFVideoSource();
|
||||
AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
class MatroskaVideoSource : public VideoBase {
|
||||
private:
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext MC;
|
||||
CompressedStream *CS;
|
||||
char ErrorMessage[256];
|
||||
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
MatroskaVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
||||
~MatroskaVideoSource();
|
||||
AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
#ifdef HAALITS
|
||||
|
||||
class HaaliTSVideoSource : public VideoBase {
|
||||
private:
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
HaaliTSVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
||||
~HaaliTSVideoSource();
|
||||
AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
#endif // HAALITS
|
||||
|
||||
#endif
|
|
@ -1,798 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 <iostream>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <errno.h>
|
||||
#include "indexing.h"
|
||||
#include "wave64writer.h"
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _fseeki64 fseeko
|
||||
#define _ftelli64 ftello
|
||||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include "MatroskaParser.h"
|
||||
#include "stdiostream.h"
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# define _WIN32_DCOM
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# include <atlbase.h>
|
||||
# include <dshow.h>
|
||||
# include "CoParser.h"
|
||||
# include <initguid.h>
|
||||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
class MatroskaAudioContext {
|
||||
public:
|
||||
Wave64Writer *W64W;
|
||||
AVCodecContext *CTX;
|
||||
CompressedStream *CS;
|
||||
int64_t CurrentSample;
|
||||
|
||||
MatroskaAudioContext() {
|
||||
W64W = NULL;
|
||||
CTX = NULL;
|
||||
CS = NULL;
|
||||
CurrentSample = 0;
|
||||
}
|
||||
|
||||
~MatroskaAudioContext() {
|
||||
delete W64W;
|
||||
if (CTX) {
|
||||
avcodec_close(CTX);
|
||||
av_free(CTX);
|
||||
}
|
||||
if (CS)
|
||||
cs_Destroy(CS);
|
||||
}
|
||||
};
|
||||
|
||||
class FFAudioContext {
|
||||
public:
|
||||
Wave64Writer *W64W;
|
||||
AVCodecContext *CTX;
|
||||
int64_t CurrentSample;
|
||||
|
||||
FFAudioContext() {
|
||||
W64W = NULL;
|
||||
CTX = NULL;
|
||||
CurrentSample = 0;
|
||||
}
|
||||
|
||||
~FFAudioContext() {
|
||||
delete W64W;
|
||||
if (CTX)
|
||||
avcodec_close(CTX);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAALITS
|
||||
class HaaliTSIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
public:
|
||||
HaaliTSIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
AudioContexts = new MatroskaAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
}
|
||||
|
||||
~HaaliTSIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class MatroskaIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext *MC;
|
||||
public:
|
||||
MatroskaIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts, MatroskaFile *MF, MatroskaReaderContext *MC) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
AudioContexts = new MatroskaAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
this->MF = MF;
|
||||
this->MC = MC;
|
||||
}
|
||||
|
||||
~MatroskaIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
mkv_Close(MF);
|
||||
fclose(MC->ST.fp);
|
||||
}
|
||||
};
|
||||
|
||||
class FFIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
FFAudioContext *AudioContexts;
|
||||
AVFormatContext *FormatContext;
|
||||
public:
|
||||
FFIndexMemory(int Tracks, int16_t *&DecodingBuffer, FFAudioContext *&AudioContexts, AVFormatContext *&FormatContext) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
AudioContexts = new FFAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
this->FormatContext = FormatContext;
|
||||
}
|
||||
|
||||
~FFIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
av_close_input_file(FormatContext);
|
||||
}
|
||||
};
|
||||
|
||||
static bool DTSComparison(FrameInfo FI1, FrameInfo FI2) {
|
||||
return FI1.DTS < FI2.DTS;
|
||||
}
|
||||
|
||||
static void SortTrackIndices(FrameIndex *TrackIndices) {
|
||||
for (FrameIndex::iterator Cur=TrackIndices->begin(); Cur!=TrackIndices->end(); Cur++)
|
||||
std::sort(Cur->begin(), Cur->end(), DTSComparison);
|
||||
}
|
||||
|
||||
int WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
|
||||
std::ofstream Index(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
|
||||
if (!Index.is_open()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", IndexFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write the index file header
|
||||
IndexHeader IH;
|
||||
IH.Id = INDEXID;
|
||||
IH.Version = INDEXVERSION;
|
||||
IH.Tracks = TrackIndices->size();
|
||||
IH.Decoder = TrackIndices->Decoder;
|
||||
Index.write(reinterpret_cast<char *>(&IH), sizeof(IH));
|
||||
|
||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||
int TT = (*TrackIndices)[i].TT;
|
||||
Index.write(reinterpret_cast<char *>(&TT), sizeof(TT));
|
||||
int Num = (*TrackIndices)[i].TB.Num;
|
||||
Index.write(reinterpret_cast<char *>(&Num), sizeof(Num));
|
||||
int Den = (*TrackIndices)[i].TB.Den;
|
||||
Index.write(reinterpret_cast<char *>(&Den), sizeof(Den));
|
||||
size_t Frames = (*TrackIndices)[i].size();
|
||||
Index.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
||||
|
||||
for (size_t j = 0; j < Frames; j++)
|
||||
Index.write(reinterpret_cast<char *>(&(TrackIndices->at(i)[j])), sizeof(FrameInfo));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAALITS
|
||||
static FrameIndex *MakeHaaliTSIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
|
||||
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
CLSID clsid = Haali_TS_Parser;
|
||||
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CComPtr<IMemAlloc> pMA;
|
||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CComPtr<IMMStream> pMS;
|
||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WCHAR WSourceFile[2048];
|
||||
mbstowcs(WSourceFile, SourceFile, 2000);
|
||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int NumTracks = 0;
|
||||
CComPtr<IEnumUnknown> pEU;
|
||||
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
|
||||
CComPtr<IUnknown> pU;
|
||||
while (pEU->Next(1, &pU, NULL) == S_OK) {
|
||||
NumTracks++;
|
||||
pU = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Audio stuff
|
||||
|
||||
int16_t *db;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
HaaliTSIndexMemory IM = HaaliTSIndexMemory(NumTracks, db, AudioContexts);
|
||||
|
||||
FrameIndex *TrackIndices = new FrameIndex();
|
||||
TrackIndices->Decoder = 2;
|
||||
|
||||
int TrackTypes[32];
|
||||
int CurrentTrack = 0;
|
||||
pEU = NULL;
|
||||
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
|
||||
CComPtr<IUnknown> pU;
|
||||
while (pEU->Next(1, &pU, NULL) == S_OK) {
|
||||
CComQIPtr<IPropertyBag> pBag = pU;
|
||||
BSTR CodecID = NULL;
|
||||
TrackTypes[CurrentTrack] = -200;
|
||||
uint8_t * CodecPrivate = NULL;
|
||||
int CodecPrivateSize = 0;
|
||||
|
||||
if (pBag) {
|
||||
VARIANT pV;
|
||||
|
||||
pV.vt = VT_EMPTY;
|
||||
if (pBag->Read(L"CodecID", &pV, NULL) == S_OK)
|
||||
CodecID = pV.bstrVal;
|
||||
|
||||
pV.vt = VT_EMPTY;
|
||||
if (pBag->Read(L"Type", &pV, NULL) == S_OK)
|
||||
TrackTypes[CurrentTrack] = pV.uintVal;
|
||||
|
||||
pV.vt = VT_EMPTY;
|
||||
if (pBag->Read(L"CodecPrivate", &pV, NULL) == S_OK) {
|
||||
CodecPrivate = (uint8_t *)pV.parray->pvData;
|
||||
CodecPrivateSize = pV.parray->cbElements;
|
||||
}
|
||||
}
|
||||
|
||||
TrackIndices->push_back(FrameInfoVector(1, 1000000000, TrackTypes[CurrentTrack] - 1));
|
||||
|
||||
if (IndexMask & (1 << CurrentTrack) && TrackTypes[CurrentTrack] == TT_AUDIO) {
|
||||
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
|
||||
AudioCodecContext->extradata = CodecPrivate;
|
||||
AudioCodecContext->extradata_size = CodecPrivateSize;
|
||||
AudioContexts[CurrentTrack].CTX = AudioCodecContext;
|
||||
|
||||
char ACodecID[2048];
|
||||
wcstombs(ACodecID, CodecID, 2000);
|
||||
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, NULL));
|
||||
if (AudioCodec == NULL) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[CurrentTrack].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[CurrentTrack].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
IndexMask &= ~(1 << CurrentTrack);
|
||||
}
|
||||
|
||||
pU = NULL;
|
||||
CurrentTrack++;
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
for (;;) {
|
||||
if (IP) {
|
||||
if ((*IP)(0, 0, 1, Private)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CComPtr<IMMFrame> pMMF;
|
||||
if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
|
||||
break;
|
||||
|
||||
REFERENCE_TIME Ts, Te;
|
||||
HRESULT hr = pMMF->GetTime(&Ts, &Te);
|
||||
|
||||
unsigned int CurrentTrack = pMMF->GetTrack();
|
||||
|
||||
// Only create index entries for video for now to save space
|
||||
if (TrackTypes[CurrentTrack] == TT_VIDEO) {
|
||||
(*TrackIndices)[CurrentTrack].push_back(FrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
|
||||
} else if (TrackTypes[CurrentTrack] == TT_AUDIO && (IndexMask & (1 << CurrentTrack))) {
|
||||
/* (*TrackIndices)[Track].push_back(FrameInfo(AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0));
|
||||
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
|
||||
|
||||
int Size = FrameSize;
|
||||
uint8_t *Data = MC.Buffer;
|
||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
|
||||
|
||||
while (Size > 0) {
|
||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
|
||||
if (Ret < 0) {
|
||||
if (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[Track].clear();
|
||||
IndexMask &= ~(1 << Track);
|
||||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (Ret > 0) {
|
||||
Size -= Ret;
|
||||
Data += Ret;
|
||||
}
|
||||
|
||||
if (dbsize > 0)
|
||||
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||
|
||||
if (dbsize > 0 && (DumpMask & (1 << Track))) {
|
||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||
if (!AudioContexts[Track].W64W) {
|
||||
char ABuf[50];
|
||||
std::string WN(AudioFile);
|
||||
int Offset = StartTime * mkv_TruncFloat(mkv_GetTrackInfo(MF, Track)->TimecodeScale) / (double)1000000;
|
||||
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Track, Offset);
|
||||
WN += ABuf;
|
||||
|
||||
AudioContexts[Track].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
|
||||
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
|
||||
}
|
||||
|
||||
AudioContexts[Track].W64W->WriteData(db, dbsize);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
}
|
||||
#endif
|
||||
|
||||
static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
|
||||
MatroskaFile *MF;
|
||||
char ErrorMessage[256];
|
||||
MatroskaReaderContext MC;
|
||||
MC.Buffer = NULL;
|
||||
MC.BufferSize = 0;
|
||||
|
||||
InitStdIoStream(&MC.ST);
|
||||
MC.ST.fp = fopen(SourceFile, "rb");
|
||||
if (MC.ST.fp == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Audio stuff
|
||||
|
||||
int16_t *db;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), db, AudioContexts, MF, &MC);
|
||||
|
||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
||||
TrackInfo *TI = mkv_GetTrackInfo(MF, i);
|
||||
if (IndexMask & (1 << i) && TI->Type == TT_AUDIO) {
|
||||
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
|
||||
AudioCodecContext->extradata = (uint8_t *)TI->CodecPrivate;
|
||||
AudioCodecContext->extradata_size = TI->CodecPrivateSize;
|
||||
AudioContexts[i].CTX = AudioCodecContext;
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
AudioContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (AudioContexts[i].CS == NULL) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[i].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||
if (AudioCodec == NULL) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[i].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[i].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
IndexMask &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
int64_t CurrentPos = _ftelli64(MC.ST.fp);
|
||||
_fseeki64(MC.ST.fp, 0, SEEK_END);
|
||||
int64_t SourceSize = _ftelli64(MC.ST.fp);
|
||||
_fseeki64(MC.ST.fp, CurrentPos, SEEK_SET);
|
||||
|
||||
FrameIndex *TrackIndices = new FrameIndex();
|
||||
TrackIndices->Decoder = 1;
|
||||
|
||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++)
|
||||
TrackIndices->push_back(FrameInfoVector(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, mkv_GetTrackInfo(MF, i)->Type - 1));
|
||||
|
||||
ulonglong StartTime, EndTime, FilePos;
|
||||
unsigned int Track, FrameFlags, FrameSize;
|
||||
|
||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||
// Update progress
|
||||
if (IP) {
|
||||
if ((*IP)(0, _ftelli64(MC.ST.fp), SourceSize, Private)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Only create index entries for video for now to save space
|
||||
if (mkv_GetTrackInfo(MF, Track)->Type == TT_VIDEO) {
|
||||
(*TrackIndices)[Track].push_back(FrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0));
|
||||
} else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) {
|
||||
(*TrackIndices)[Track].push_back(FrameInfo(AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0));
|
||||
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
|
||||
|
||||
int Size = FrameSize;
|
||||
uint8_t *Data = MC.Buffer;
|
||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
|
||||
|
||||
while (Size > 0) {
|
||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
|
||||
if (Ret < 0) {
|
||||
if (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[Track].clear();
|
||||
IndexMask &= ~(1 << Track);
|
||||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (Ret > 0) {
|
||||
Size -= Ret;
|
||||
Data += Ret;
|
||||
}
|
||||
|
||||
if (dbsize > 0)
|
||||
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||
|
||||
if (dbsize > 0 && (DumpMask & (1 << Track))) {
|
||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||
if (!AudioContexts[Track].W64W) {
|
||||
char ABuf[50];
|
||||
std::string WN(AudioFile);
|
||||
int Offset = StartTime * mkv_TruncFloat(mkv_GetTrackInfo(MF, Track)->TimecodeScale) / (double)1000000;
|
||||
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Track, Offset);
|
||||
WN += ABuf;
|
||||
|
||||
AudioContexts[Track].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
|
||||
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
|
||||
}
|
||||
|
||||
AudioContexts[Track].W64W->WriteData(db, dbsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
}
|
||||
|
||||
FrameIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
|
||||
AVFormatContext *FormatContext = NULL;
|
||||
IndexMask |= DumpMask;
|
||||
|
||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s'", SourceFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Do matroska indexing instead?
|
||||
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
||||
av_close_input_file(FormatContext);
|
||||
return MakeMatroskaIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
#ifdef HAALITS
|
||||
// Do haali ts indexing instead?
|
||||
if (!strcmp(FormatContext->iformat->name, "mpegts")) {
|
||||
av_close_input_file(FormatContext);
|
||||
return MakeHaaliTSIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (av_find_stream_info(FormatContext) < 0) {
|
||||
av_close_input_file(FormatContext);
|
||||
_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Audio stuff
|
||||
|
||||
int16_t *db;
|
||||
FFAudioContext *AudioContexts;
|
||||
FFIndexMemory IM = FFIndexMemory(FormatContext->nb_streams, db, AudioContexts, FormatContext);
|
||||
|
||||
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
|
||||
if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
|
||||
AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec;
|
||||
|
||||
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
|
||||
if (AudioCodec == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AudioContexts[i].CTX = AudioCodecContext;
|
||||
} else {
|
||||
IndexMask &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
FrameIndex *TrackIndices = new FrameIndex();
|
||||
TrackIndices->Decoder = 0;
|
||||
|
||||
for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
|
||||
TrackIndices->push_back(FrameInfoVector(FormatContext->streams[i]->time_base.num * 1000,
|
||||
FormatContext->streams[i]->time_base.den,
|
||||
FormatContext->streams[i]->codec->codec_type));
|
||||
|
||||
AVPacket Packet;
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
// Update progress
|
||||
if (IP) {
|
||||
if ((*IP)(0, FormatContext->pb->pos, FormatContext->file_size, Private)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Only create index entries for video for now to save space
|
||||
if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_VIDEO) {
|
||||
(*TrackIndices)[Packet.stream_index].push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0));
|
||||
} else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) {
|
||||
(*TrackIndices)[Packet.stream_index].push_back(FrameInfo(Packet.dts, AudioContexts[Packet.stream_index].CurrentSample, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0));
|
||||
AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec;
|
||||
int Size = Packet.size;
|
||||
uint8_t *Data = Packet.data;
|
||||
|
||||
while (Size > 0) {
|
||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
|
||||
if (Ret < 0) {
|
||||
if (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[Packet.stream_index].clear();
|
||||
IndexMask &= ~(1 << Packet.stream_index);
|
||||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (Ret > 0) {
|
||||
Size -= Ret;
|
||||
Data += Ret;
|
||||
}
|
||||
|
||||
if (dbsize > 0)
|
||||
AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||
|
||||
if (dbsize > 0 && (DumpMask & (1 << Packet.stream_index))) {
|
||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||
if (!AudioContexts[Packet.stream_index].W64W) {
|
||||
char ABuf[50];
|
||||
std::string WN(AudioFile);
|
||||
int Offset = (Packet.dts * FormatContext->streams[Packet.stream_index]->time_base.num)
|
||||
/ (double)(FormatContext->streams[Packet.stream_index]->time_base.den * 1000);
|
||||
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Packet.stream_index, Offset);
|
||||
WN += ABuf;
|
||||
|
||||
AudioContexts[Packet.stream_index].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
|
||||
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
|
||||
}
|
||||
|
||||
AudioContexts[Packet.stream_index].W64W->WriteData(db, dbsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
}
|
||||
|
||||
FrameIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
std::ifstream Index(IndexFile, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!Index.is_open()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read the index file header
|
||||
IndexHeader IH;
|
||||
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
|
||||
if (IH.Id != INDEXID) {
|
||||
_snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IH.Version != INDEXVERSION) {
|
||||
_snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FrameIndex *TrackIndices = new FrameIndex();
|
||||
|
||||
try {
|
||||
|
||||
TrackIndices->Decoder = IH.Decoder;
|
||||
|
||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||
// Read how many records belong to the current stream
|
||||
int TT;
|
||||
Index.read(reinterpret_cast<char *>(&TT), sizeof(TT));
|
||||
int Num;
|
||||
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
|
||||
int Den;
|
||||
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
|
||||
size_t Frames;
|
||||
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
||||
TrackIndices->push_back(FrameInfoVector(Num, Den, TT));
|
||||
|
||||
FrameInfo FI(0, false);
|
||||
for (size_t j = 0; j < Frames; j++) {
|
||||
Index.read(reinterpret_cast<char *>(&FI), sizeof(FrameInfo));
|
||||
TrackIndices->at(i).push_back(FI);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
delete TrackIndices;
|
||||
_snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TrackIndices;
|
||||
}
|
||||
|
||||
int FrameInfoVector::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
std::ofstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!Timecodes.is_open()) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Timecodes << "# timecode format v2\n";
|
||||
|
||||
for (iterator Cur=begin(); Cur!=end(); Cur++)
|
||||
Timecodes << (int64_t)((Cur->DTS * TB.Num) / (double)TB.Den) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FrameInfoVector::FrameFromDTS(int64_t DTS) {
|
||||
for (int i = 0; i < static_cast<int>(size()); i++)
|
||||
if (at(i).DTS == DTS)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FrameInfoVector::ClosestFrameFromDTS(int64_t DTS) {
|
||||
int Frame = 0;
|
||||
int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number
|
||||
for (int i = 0; i < static_cast<int>(size()); i++) {
|
||||
int64_t CurrentDiff = FFABS(at(i).DTS - DTS);
|
||||
if (CurrentDiff < BestDiff) {
|
||||
BestDiff = CurrentDiff;
|
||||
Frame = i;
|
||||
}
|
||||
}
|
||||
|
||||
//int64_t tmp = at(2).DTS - at(1).DTS;
|
||||
//ATLASSERT(BestDiff == 0);
|
||||
return Frame;
|
||||
}
|
||||
|
||||
int FrameInfoVector::FindClosestKeyFrame(int Frame) {
|
||||
Frame = FFMIN(FFMAX(Frame, 0), size() - 1);
|
||||
for (int i = Frame; i > 0; i--)
|
||||
if (at(i).KeyFrame)
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FrameInfoVector::FrameInfoVector() {
|
||||
this->TT = 0;
|
||||
this->TB.Num = 0;
|
||||
this->TB.Den = 0;
|
||||
}
|
||||
|
||||
FrameInfoVector::FrameInfoVector(int Num, int Den, int TT) {
|
||||
this->TT = TT;
|
||||
this->TB.Num = Num;
|
||||
this->TB.Den = Den;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 INDEXING_H
|
||||
#define INDEXING_H
|
||||
|
||||
#include <vector>
|
||||
#include "utils.h"
|
||||
#include "ffms.h"
|
||||
|
||||
#define INDEXVERSION 11
|
||||
#define INDEXID 0x53920873
|
||||
|
||||
struct IndexHeader {
|
||||
uint32_t Id;
|
||||
uint32_t Version;
|
||||
uint32_t Tracks;
|
||||
uint32_t Decoder;
|
||||
};
|
||||
|
||||
class FrameInfoVector : public std::vector<FrameInfo> {
|
||||
public:
|
||||
int TT;
|
||||
TrackTimeBase TB;
|
||||
|
||||
int FindClosestKeyFrame(int Frame);
|
||||
int FrameFromDTS(int64_t DTS);
|
||||
int ClosestFrameFromDTS(int64_t DTS);
|
||||
int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
||||
|
||||
FrameInfoVector();
|
||||
FrameInfoVector(int Num, int Den, int TT);
|
||||
};
|
||||
|
||||
class FrameIndex : public std::vector<FrameInfoVector> {
|
||||
public:
|
||||
int Decoder;
|
||||
};
|
||||
|
||||
FrameIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize);
|
||||
FrameIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
int WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
|
||||
|
||||
#endif
|
|
@ -1,444 +0,0 @@
|
|||
// Copyright (c) 2007-2008 Fredrik Mellbin
|
||||
//
|
||||
// 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 "utils.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifdef _MSC_VER
|
||||
# include <intrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _fseeki64 fseeko
|
||||
#define _ftelli64 ftello
|
||||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
int GetCPUFlags() {
|
||||
// FIXME Add proper feature detection when msvc isn't used
|
||||
int Flags = PP_CPU_CAPS_MMX | PP_CPU_CAPS_MMX2;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
Flags = 0;
|
||||
int CPUInfo[4];
|
||||
__cpuid(CPUInfo, 0);
|
||||
|
||||
// PP and SWS defines have the same values for their defines so this actually works
|
||||
if (CPUInfo[3] & (1 << 23))
|
||||
Flags |= PP_CPU_CAPS_MMX;
|
||||
if (CPUInfo[3] & (1 << 25))
|
||||
Flags |= PP_CPU_CAPS_MMX2;
|
||||
#endif
|
||||
|
||||
return Flags;
|
||||
}
|
||||
|
||||
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (CS) {
|
||||
char CSBuffer[4096];
|
||||
|
||||
unsigned int DecompressedFrameSize = 0;
|
||||
|
||||
cs_NextFrame(CS, FilePos, FrameSize);
|
||||
|
||||
for (;;) {
|
||||
int ReadBytes = cs_ReadData(CS, CSBuffer, sizeof(CSBuffer));
|
||||
if (ReadBytes < 0) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Error decompressing data: %s", cs_GetLastError(CS));
|
||||
return 1;
|
||||
}
|
||||
if (ReadBytes == 0) {
|
||||
FrameSize = DecompressedFrameSize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Context.BufferSize < DecompressedFrameSize + ReadBytes) {
|
||||
Context.BufferSize = FrameSize;
|
||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
||||
if (Context.Buffer == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Out of memory");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(Context.Buffer + DecompressedFrameSize, CSBuffer, ReadBytes);
|
||||
DecompressedFrameSize += ReadBytes;
|
||||
}
|
||||
} else {
|
||||
if (_fseeki64(Context.ST.fp, FilePos, SEEK_SET)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "fseek(): %s", strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (Context.BufferSize < FrameSize) {
|
||||
Context.BufferSize = FrameSize;
|
||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
||||
if (Context.Buffer == NULL) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Out of memory");
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp);
|
||||
if (ReadBytes != FrameSize) {
|
||||
if (ReadBytes == 0) {
|
||||
if (feof(Context.ST.fp)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Unexpected EOF while reading frame");
|
||||
return 5;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Error reading frame: %s", strerror(errno));
|
||||
return 6;
|
||||
}
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Short read while reading frame");
|
||||
return 7;
|
||||
}
|
||||
_snprintf(ErrorMsg, MsgSize, "Unknown read error");
|
||||
return 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FrameSize = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AudioFMTIsFloat(SampleFormat FMT){
|
||||
switch (FMT) {
|
||||
case SAMPLE_FMT_FLT:
|
||||
case SAMPLE_FMT_DBL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency
|
||||
typedef struct BITMAPINFOHEADER {
|
||||
uint32_t biSize;
|
||||
int32_t biWidth;
|
||||
int32_t biHeight;
|
||||
uint16_t biPlanes;
|
||||
uint16_t biBitCount;
|
||||
uint32_t biCompression;
|
||||
uint32_t biSizeImage;
|
||||
int32_t biXPelsPerMeter;
|
||||
int32_t biYPelsPerMeter;
|
||||
uint32_t biClrUsed;
|
||||
uint32_t biClrImportant;
|
||||
} BITMAPINFOHEADER;
|
||||
|
||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3)\
|
||||
((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |\
|
||||
((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
|
||||
|
||||
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate) {
|
||||
/* Video Codecs */
|
||||
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
|
||||
// fourcc list from ffdshow
|
||||
switch (((BITMAPINFOHEADER *)CodecPrivate)->biCompression) {
|
||||
case MAKEFOURCC('F', 'F', 'D', 'S'):
|
||||
case MAKEFOURCC('F', 'V', 'F', 'W'):
|
||||
case MAKEFOURCC('X', 'V', 'I', 'D'):
|
||||
case MAKEFOURCC('D', 'I', 'V', 'X'):
|
||||
case MAKEFOURCC('D', 'X', '5', '0'):
|
||||
case MAKEFOURCC('M', 'P', '4', 'V'):
|
||||
case MAKEFOURCC('m', 'p', '4', 'v'): // This one may be my fault
|
||||
case MAKEFOURCC('3', 'I', 'V', 'X'):
|
||||
case MAKEFOURCC('W', 'V', '1', 'F'):
|
||||
case MAKEFOURCC('F', 'M', 'P', '4'):
|
||||
case MAKEFOURCC('S', 'M', 'P', '4'):
|
||||
return CODEC_ID_MPEG4;
|
||||
case MAKEFOURCC('D', 'I', 'V', '3'):
|
||||
case MAKEFOURCC('D', 'V', 'X', '3'):
|
||||
case MAKEFOURCC('M', 'P', '4', '3'):
|
||||
return CODEC_ID_MSMPEG4V3;
|
||||
case MAKEFOURCC('M', 'P', '4', '2'):
|
||||
return CODEC_ID_MSMPEG4V2;
|
||||
case MAKEFOURCC('M', 'P', '4', '1'):
|
||||
return CODEC_ID_MSMPEG4V1;
|
||||
case MAKEFOURCC('W', 'M', 'V', '1'):
|
||||
return CODEC_ID_WMV1;
|
||||
case MAKEFOURCC('W', 'M', 'V', '2'):
|
||||
return CODEC_ID_WMV2;
|
||||
case MAKEFOURCC('W', 'M', 'V', '3'):
|
||||
return CODEC_ID_WMV3;
|
||||
/*
|
||||
case MAKEFOURCC('M', 'S', 'S', '1'):
|
||||
case MAKEFOURCC('M', 'S', 'S', '2'):
|
||||
case MAKEFOURCC('W', 'V', 'P', '2'):
|
||||
case MAKEFOURCC('W', 'M', 'V', 'P'):
|
||||
return CODEC_ID_WMV9_LIB;
|
||||
*/
|
||||
case MAKEFOURCC('W', 'V', 'C', '1'):
|
||||
return CODEC_ID_VC1;
|
||||
case MAKEFOURCC('V', 'P', '5', '0'):
|
||||
return CODEC_ID_VP5;
|
||||
case MAKEFOURCC('V', 'P', '6', '0'):
|
||||
case MAKEFOURCC('V', 'P', '6', '1'):
|
||||
case MAKEFOURCC('V', 'P', '6', '2'):
|
||||
return CODEC_ID_VP6;
|
||||
case MAKEFOURCC('V', 'P', '6', 'F'):
|
||||
case MAKEFOURCC('F', 'L', 'V', '4'):
|
||||
return CODEC_ID_VP6F;
|
||||
case MAKEFOURCC('C', 'A', 'V', 'S'):
|
||||
return CODEC_ID_CAVS;
|
||||
case MAKEFOURCC('M', 'P', 'G', '1'):
|
||||
case MAKEFOURCC('M', 'P', 'E', 'G'):
|
||||
return CODEC_ID_MPEG2VIDEO; // not a typo
|
||||
case MAKEFOURCC('M', 'P', 'G', '2'):
|
||||
case MAKEFOURCC('E', 'M', '2', 'V'):
|
||||
case MAKEFOURCC('M', 'M', 'E', 'S'):
|
||||
return CODEC_ID_MPEG2VIDEO;
|
||||
case MAKEFOURCC('H', '2', '6', '3'):
|
||||
case MAKEFOURCC('S', '2', '6', '3'):
|
||||
case MAKEFOURCC('L', '2', '6', '3'):
|
||||
case MAKEFOURCC('M', '2', '6', '3'):
|
||||
case MAKEFOURCC('U', '2', '6', '3'):
|
||||
case MAKEFOURCC('X', '2', '6', '3'):
|
||||
return CODEC_ID_H263;
|
||||
case MAKEFOURCC('H', '2', '6', '4'):
|
||||
case MAKEFOURCC('X', '2', '6', '4'):
|
||||
case MAKEFOURCC('V', 'S', 'S', 'H'):
|
||||
case MAKEFOURCC('D', 'A', 'V', 'C'):
|
||||
case MAKEFOURCC('P', 'A', 'V', 'C'):
|
||||
case MAKEFOURCC('A', 'V', 'C', '1'):
|
||||
return CODEC_ID_H264;
|
||||
case MAKEFOURCC('M', 'J', 'P', 'G'):
|
||||
case MAKEFOURCC('L', 'J', 'P', 'G'):
|
||||
case MAKEFOURCC('M', 'J', 'L', 'S'):
|
||||
case MAKEFOURCC('J', 'P', 'E', 'G'): // questionable fourcc?
|
||||
case MAKEFOURCC('A', 'V', 'R', 'N'):
|
||||
case MAKEFOURCC('M', 'J', 'P', 'A'):
|
||||
return CODEC_ID_MJPEG;
|
||||
case MAKEFOURCC('D', 'V', 'S', 'D'):
|
||||
case MAKEFOURCC('D', 'V', '2', '5'):
|
||||
case MAKEFOURCC('D', 'V', '5', '0'):
|
||||
case MAKEFOURCC('C', 'D', 'V', 'C'):
|
||||
case MAKEFOURCC('C', 'D', 'V', '5'):
|
||||
case MAKEFOURCC('D', 'V', 'I', 'S'):
|
||||
case MAKEFOURCC('P', 'D', 'V', 'C'):
|
||||
return CODEC_ID_DVVIDEO;
|
||||
case MAKEFOURCC('H', 'F', 'Y', 'U'):
|
||||
return CODEC_ID_HUFFYUV;
|
||||
case MAKEFOURCC('F', 'F', 'V', 'H'):
|
||||
return CODEC_ID_FFVHUFF;
|
||||
case MAKEFOURCC('C', 'Y', 'U', 'V'):
|
||||
return CODEC_ID_CYUV;
|
||||
case MAKEFOURCC('A', 'S', 'V', '1'):
|
||||
return CODEC_ID_ASV1;
|
||||
case MAKEFOURCC('A', 'S', 'V', '2'):
|
||||
return CODEC_ID_ASV2;
|
||||
case MAKEFOURCC('V', 'C', 'R', '1'):
|
||||
return CODEC_ID_VCR1;
|
||||
case MAKEFOURCC('T', 'H', 'E', 'O'):
|
||||
return CODEC_ID_THEORA;
|
||||
case MAKEFOURCC('S', 'V', 'Q', '1'):
|
||||
return CODEC_ID_SVQ1;
|
||||
case MAKEFOURCC('S', 'V', 'Q', '3'):
|
||||
return CODEC_ID_SVQ3;
|
||||
case MAKEFOURCC('R', 'P', 'Z', 'A'):
|
||||
return CODEC_ID_RPZA;
|
||||
case MAKEFOURCC('F', 'F', 'V', '1'):
|
||||
return CODEC_ID_FFV1;
|
||||
case MAKEFOURCC('V', 'P', '3', '1'):
|
||||
return CODEC_ID_VP3;
|
||||
case MAKEFOURCC('R', 'L', 'E', '8'):
|
||||
return CODEC_ID_MSRLE;
|
||||
case MAKEFOURCC('M', 'S', 'Z', 'H'):
|
||||
return CODEC_ID_MSZH;
|
||||
case MAKEFOURCC('Z', 'L', 'I', 'B'):
|
||||
return CODEC_ID_ZLIB;
|
||||
case MAKEFOURCC('F', 'L', 'V', '1'):
|
||||
return CODEC_ID_FLV1;
|
||||
/*
|
||||
case MAKEFOURCC('P', 'N', 'G', '1'):
|
||||
return CODEC_ID_COREPNG;
|
||||
*/
|
||||
case MAKEFOURCC('M', 'P', 'N', 'G'):
|
||||
return CODEC_ID_PNG;
|
||||
/*
|
||||
case MAKEFOURCC('A', 'V', 'I', 'S'):
|
||||
return CODEC_ID_AVISYNTH;
|
||||
*/
|
||||
case MAKEFOURCC('C', 'R', 'A', 'M'):
|
||||
return CODEC_ID_MSVIDEO1;
|
||||
case MAKEFOURCC('R', 'T', '2', '1'):
|
||||
return CODEC_ID_INDEO2;
|
||||
case MAKEFOURCC('I', 'V', '3', '2'):
|
||||
case MAKEFOURCC('I', 'V', '3', '1'):
|
||||
return CODEC_ID_INDEO3;
|
||||
case MAKEFOURCC('C', 'V', 'I', 'D'):
|
||||
return CODEC_ID_CINEPAK;
|
||||
case MAKEFOURCC('R', 'V', '1', '0'):
|
||||
return CODEC_ID_RV10;
|
||||
case MAKEFOURCC('R', 'V', '2', '0'):
|
||||
return CODEC_ID_RV20;
|
||||
case MAKEFOURCC('8', 'B', 'P', 'S'):
|
||||
return CODEC_ID_8BPS;
|
||||
case MAKEFOURCC('Q', 'R', 'L', 'E'):
|
||||
return CODEC_ID_QTRLE;
|
||||
case MAKEFOURCC('D', 'U', 'C', 'K'):
|
||||
return CODEC_ID_TRUEMOTION1;
|
||||
case MAKEFOURCC('T', 'M', '2', '0'):
|
||||
return CODEC_ID_TRUEMOTION2;
|
||||
case MAKEFOURCC('T', 'S', 'C', 'C'):
|
||||
return CODEC_ID_TSCC;
|
||||
case MAKEFOURCC('S', 'N', 'O', 'W'):
|
||||
return CODEC_ID_SNOW;
|
||||
case MAKEFOURCC('Q', 'P', 'E', 'G'):
|
||||
case MAKEFOURCC('Q', '1', '_', '0'):
|
||||
case MAKEFOURCC('Q', '1', '_', '1'):
|
||||
return CODEC_ID_QPEG;
|
||||
case MAKEFOURCC('H', '2', '6', '1'):
|
||||
case MAKEFOURCC('M', '2', '6', '1'):
|
||||
return CODEC_ID_H261;
|
||||
case MAKEFOURCC('L', 'O', 'C', 'O'):
|
||||
return CODEC_ID_LOCO;
|
||||
case MAKEFOURCC('W', 'N', 'V', '1'):
|
||||
return CODEC_ID_WNV1;
|
||||
case MAKEFOURCC('C', 'S', 'C', 'D'):
|
||||
return CODEC_ID_CSCD;
|
||||
case MAKEFOURCC('Z', 'M', 'B', 'V'):
|
||||
return CODEC_ID_ZMBV;
|
||||
case MAKEFOURCC('U', 'L', 'T', 'I'):
|
||||
return CODEC_ID_ULTI;
|
||||
case MAKEFOURCC('V', 'I', 'X', 'L'):
|
||||
return CODEC_ID_VIXL;
|
||||
case MAKEFOURCC('A', 'A', 'S', 'C'):
|
||||
return CODEC_ID_AASC;
|
||||
case MAKEFOURCC('F', 'P', 'S', '1'):
|
||||
return CODEC_ID_FRAPS;
|
||||
default:
|
||||
return CODEC_ID_NONE;
|
||||
}
|
||||
|
||||
} else if (!strcmp(Codec, "V_MPEG4/ISO/AVC"))
|
||||
return CODEC_ID_H264;
|
||||
else if (!strcmp(Codec, "V_MPEG4/ISO/AP"))
|
||||
return CODEC_ID_MPEG4;
|
||||
else if (!strcmp(Codec, "V_MPEG4/ISO/ASP"))
|
||||
return CODEC_ID_MPEG4;
|
||||
else if (!strcmp(Codec, "V_MPEG4/ISO/SP"))
|
||||
return CODEC_ID_MPEG4;
|
||||
else if (!strcmp(Codec, "V_MPEG4/MS/V3"))
|
||||
return CODEC_ID_MSMPEG4V3;
|
||||
else if (!strcmp(Codec, "V_MPEG2"))
|
||||
return CODEC_ID_MPEG2VIDEO;
|
||||
else if (!strcmp(Codec, "V_MPEG1"))
|
||||
return CODEC_ID_MPEG2VIDEO; // still not a typo
|
||||
else if (!strcmp(Codec, "V_VC1"))
|
||||
return CODEC_ID_VC1;
|
||||
else if (!strcmp(Codec, "V_SNOW"))
|
||||
return CODEC_ID_SNOW;
|
||||
else if (!strcmp(Codec, "V_THEORA"))
|
||||
return CODEC_ID_THEORA;
|
||||
else if (!strcmp(Codec, "V_UNCOMPRESSED"))
|
||||
return CODEC_ID_NONE; // bleh
|
||||
else if (!strcmp(Codec, "V_QUICKTIME"))
|
||||
return CODEC_ID_SVQ3; // no idea if this is right
|
||||
else if (!strcmp(Codec, "V_CIPC"))
|
||||
return CODEC_ID_NONE; // don't know, don't care
|
||||
else if (!strncmp(Codec, "V_REAL/RV", 9)) {
|
||||
switch (Codec[9]) {
|
||||
case '1':
|
||||
return CODEC_ID_RV10;
|
||||
case '2':
|
||||
return CODEC_ID_RV20;
|
||||
case '3':
|
||||
return CODEC_ID_RV30;
|
||||
case '4':
|
||||
return CODEC_ID_RV40;
|
||||
default:
|
||||
return CODEC_ID_NONE;
|
||||
}
|
||||
/* Audio Codecs */
|
||||
} else if (!strcmp(Codec, "A_AC3"))
|
||||
return CODEC_ID_AC3;
|
||||
else if (!strcmp(Codec, "A_EAC3"))
|
||||
return CODEC_ID_AC3;
|
||||
else if (!strcmp(Codec, "A_MPEG/L3"))
|
||||
return CODEC_ID_MP3;
|
||||
else if (!strcmp(Codec, "A_MPEG/L2"))
|
||||
return CODEC_ID_MP2;
|
||||
else if (!strcmp(Codec, "A_MPEG/L1"))
|
||||
return CODEC_ID_MP2; // correct?
|
||||
else if (!strcmp(Codec, "A_DTS"))
|
||||
return CODEC_ID_DTS;
|
||||
else if (!strcmp(Codec, "A_PCM/INT/LIT")) {
|
||||
/* FIXME
|
||||
switch (TI->AV.Audio.BitDepth) {
|
||||
case 8: return CODEC_ID_PCM_S8;
|
||||
case 16: return CODEC_ID_PCM_S16LE;
|
||||
case 24: return CODEC_ID_PCM_S24LE;
|
||||
case 32: return CODEC_ID_PCM_S32LE;
|
||||
default: return CODEC_ID_NONE;
|
||||
}
|
||||
*/
|
||||
return CODEC_ID_NONE;
|
||||
} else if (!strcmp(Codec, "A_PCM/INT/BIG")) {
|
||||
/* FIXME
|
||||
switch (TI->AV.Audio.BitDepth) {
|
||||
case 8: return CODEC_ID_PCM_S8;
|
||||
case 16: return CODEC_ID_PCM_S16BE;
|
||||
case 24: return CODEC_ID_PCM_S24BE;
|
||||
case 32: return CODEC_ID_PCM_S32BE;
|
||||
default: return CODEC_ID_NONE;
|
||||
}
|
||||
*/
|
||||
return CODEC_ID_NONE;
|
||||
} else if (!strcmp(Codec, "A_PCM/FLOAT/IEEE"))
|
||||
return CODEC_ID_PCM_F32LE; // only a most likely guess, may do bad things
|
||||
else if (!strcmp(Codec, "A_FLAC"))
|
||||
return CODEC_ID_FLAC;
|
||||
else if (!strcmp(Codec, "A_MPC"))
|
||||
return CODEC_ID_MUSEPACK8; // or is it CODEC_ID_MUSEPACK7? both?
|
||||
else if (!strcmp(Codec, "A_TTA1"))
|
||||
return CODEC_ID_TTA;
|
||||
else if (!strcmp(Codec, "A_WAVPACK4"))
|
||||
return CODEC_ID_WAVPACK;
|
||||
else if (!strcmp(Codec, "A_VORBIS"))
|
||||
return CODEC_ID_VORBIS;
|
||||
else if (!strcmp(Codec, "A_REAL/14_4"))
|
||||
return CODEC_ID_RA_144;
|
||||
else if (!strcmp(Codec, "A_REAL/28_8"))
|
||||
return CODEC_ID_RA_288;
|
||||
else if (!strcmp(Codec, "A_REAL/COOK"))
|
||||
return CODEC_ID_COOK;
|
||||
else if (!strcmp(Codec, "A_REAL/SIPR"))
|
||||
return CODEC_ID_NONE; // no sipr codec id?
|
||||
else if (!strcmp(Codec, "A_REAL/ATRC"))
|
||||
return CODEC_ID_ATRAC3;
|
||||
else if (!strncmp(Codec, "A_AAC", 5))
|
||||
return CODEC_ID_AAC;
|
||||
else if (!strcmp(Codec, "A_SPEEX"))
|
||||
return CODEC_ID_SPEEX;
|
||||
else if (!strcmp(Codec, "A_QUICKTIME"))
|
||||
return CODEC_ID_NONE; // no
|
||||
else if (!strcmp(Codec, "A_MS/ACM")) {
|
||||
// nothing useful here anyway?
|
||||
//#include "Mmreg.h"
|
||||
//((WAVEFORMATEX *)TI->CodecPrivate)->wFormatTag
|
||||
return CODEC_ID_NONE;
|
||||
} else {
|
||||
return CODEC_ID_NONE;
|
||||
}
|
||||
}
|
63
Makefile.am
63
Makefile.am
|
@ -1,63 +0,0 @@
|
|||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
|
||||
if HAVE_UNIVCHARDET
|
||||
univchardet = universalchardet
|
||||
endif
|
||||
|
||||
if HAVE_AUTO3_LUA
|
||||
auto3 = auto3
|
||||
endif
|
||||
|
||||
if WITH_LIBASS
|
||||
libass = libass
|
||||
endif
|
||||
|
||||
if HAVE_PROVIDER_FFMPEGSOURCE
|
||||
ffmpegsource = FFmpegSource2
|
||||
endif
|
||||
|
||||
SUBDIRS = \
|
||||
$(univchardet) \
|
||||
$(auto3) \
|
||||
$(libass) \
|
||||
$(ffmpegsource) \
|
||||
aegisub \
|
||||
automation \
|
||||
po \
|
||||
desktop
|
||||
|
||||
osx-bundle:
|
||||
@SHELL@ scripts/osx-bundle.sh @PACKAGE_TARNAME@-@PACKAGE_VERSION@ @AEGISUB_VERSION_DATA@
|
||||
|
||||
osx-dmg:
|
||||
@SHELL@ scripts/osx-dmg.sh @PACKAGE_TARNAME@-@PACKAGE_VERSION@ "@PACKAGE_TARNAME@ @PACKAGE_VERSION@"
|
||||
|
||||
EXTRA_DIST = \
|
||||
intltool-extract.in \
|
||||
intltool-merge.in \
|
||||
intltool-update.in \
|
||||
config.guess \
|
||||
config.sub \
|
||||
depcomp \
|
||||
install-sh \
|
||||
missing \
|
||||
INSTALL \
|
||||
README \
|
||||
svn_revision
|
||||
|
||||
# m4macros/
|
||||
EXTRA_DIST += \
|
||||
m4macros/check_gnu_make.m4 \
|
||||
m4macros/acx_pthread.m4 \
|
||||
m4macros/ac_agi.m4 \
|
||||
m4macros/ax_lang_compiler_ms.m4 \
|
||||
m4macros/ac_agi_mdcpucfg.m4 \
|
||||
m4macros/ax_openmp.m4 \
|
||||
m4macros/ax_check_gl.m4 \
|
||||
m4macros/ac_flag.m4
|
||||
|
||||
CLEANFILES= \
|
||||
intltool-extract \
|
||||
intltool-merge \
|
||||
intltool-update
|
|
@ -1,355 +1,90 @@
|
|||
AUTOMAKE_OPTIONS = foreign
|
||||
SUFFIXES = .c .cpp .rc
|
||||
noinst_LIBRARIES=
|
||||
|
||||
if BUILD_DARWIN
|
||||
libosxutil_subdir = libosxutil
|
||||
libosxutil_lib = libosxutil/libosxutil.a
|
||||
libosxutil_ldflags = -framework CoreFoundation
|
||||
|
||||
if HAVE_UNIVCHARDET
|
||||
univchardet = universalchardet
|
||||
endif
|
||||
|
||||
SUBDIRS = bitmaps libresrc $(libosxutil_subdir)
|
||||
|
||||
AM_CXXFLAGS = -DAEGISUB -Iinclude @WX_CPPFLAGS@ @OPENMP_CXXFLAGS@ @LIBAVFORMAT_CFLAGS@ @LIBAVCODEC_CFLAGS@ @LIBSWSCALE_CFLAGS@ @LIBAVUTIL_CFLAGS@
|
||||
|
||||
bin_PROGRAMS = aegisub-2.1
|
||||
aegisub_2_1_LDADD = libresrc/libresrc.a $(libosxutil_lib)
|
||||
aegisub_2_1_CPPFLAGS = @FREETYPE_CFLAGS@
|
||||
aegisub_2_1_LDFLAGS = @DEBUG_FLAGS@ @PROFILE_FLAGS@ @GL_LIBS@ @PTHREAD_LIBS@ @WX_LIBS@ @ICONV_LDFLAGS@ $(libosxutil_ldflags)
|
||||
LIBS += @FREETYPE_LIBS@ @FONTCONFIG_LIBS@
|
||||
|
||||
if BUILD_DARWIN
|
||||
aegisub_2_1_LDFLAGS += -L/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries
|
||||
endif
|
||||
|
||||
if HAVE_ALSA
|
||||
noinst_LIBRARIES += libaudio_alsa.a
|
||||
libaudio_alsa_a_SOURCES = audio_player_alsa.cpp
|
||||
aegisub_2_1_LDFLAGS += @ALSA_LDFLAGS@
|
||||
aegisub_2_1_LDADD += libaudio_alsa.a
|
||||
endif
|
||||
|
||||
if HAVE_PORTAUDIO
|
||||
noinst_LIBRARIES += libaudio_portaudio.a
|
||||
libaudio_portaudio_a_SOURCES = audio_player_portaudio.cpp
|
||||
libaudio_portaudio_a_CPPFLAGS = @PORTAUDIO_CFLAGS@
|
||||
aegisub_2_1_LDFLAGS += @PORTAUDIO_LDFLAGS@
|
||||
aegisub_2_1_LDADD += libaudio_portaudio.a
|
||||
endif
|
||||
|
||||
if HAVE_PULSEAUDIO
|
||||
noinst_LIBRARIES += libaudio_pulseaudio.a
|
||||
libaudio_pulseaudio_a_SOURCES = audio_player_pulse.cpp
|
||||
libaudio_pulseaudio_a_CPPFLAGS = @LIBPULSE_CFLAGS@
|
||||
aegisub_2_1_LDFLAGS += @LIBPULSE_LIBS@
|
||||
aegisub_2_1_LDADD += libaudio_pulseaudio.a
|
||||
endif
|
||||
|
||||
if HAVE_OPENAL
|
||||
noinst_LIBRARIES += libaudio_openal.a
|
||||
libaudio_openal_a_SOURCES = audio_player_openal.cpp
|
||||
libaudio_openal_a_CPPFLAGS = @OPENAL_CFLAGS@
|
||||
aegisub_2_1_LDFLAGS += @OPENAL_LIBS@
|
||||
aegisub_2_1_LDADD += libaudio_openal.a
|
||||
endif
|
||||
|
||||
if HAVE_FFMPEG
|
||||
aegisub_2_1_LDFLAGS += @LIBAVFORMAT_LIBS@ @LIBAVCODEC_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@
|
||||
endif
|
||||
|
||||
if HAVE_PROVIDER_FFMPEG
|
||||
noinst_LIBRARIES += libaudiovideo_ffmpeg.a
|
||||
libaudiovideo_ffmpeg_a_SOURCES = audio_provider_lavc.cpp lavc_file.cpp video_provider_lavc.cpp lavc_keyframes.cpp
|
||||
libaudiovideo_ffmpeg_a_CPPFLAGS = @LIBAVFORMAT_CFLAGS@ @LIBAVCODEC_CFLAGS@ @LIBSWSCALE_CFLAGS@ @LIBAVUTIL_CFLAGS@
|
||||
aegisub_2_1_LDADD += libaudiovideo_ffmpeg.a
|
||||
if WITH_LIBASS
|
||||
libass = libass
|
||||
endif
|
||||
|
||||
if HAVE_PROVIDER_FFMPEGSOURCE
|
||||
noinst_LIBRARIES += libaudiovideo_ffmpegsource.a
|
||||
libaudiovideo_ffmpegsource_a_SOURCES = audio_provider_ffmpegsource.cpp video_provider_ffmpegsource.cpp ffmpegsource_common.cpp
|
||||
libaudiovideo_ffmpegsource_a_CPPFLAGS = @LIBAVFORMAT_CFLAGS@ @LIBAVCODEC_CFLAGS@ @LIBSWSCALE_CFLAGS@ @LIBAVUTIL_CFLAGS@ @LIBPOSTPROC_CFLAGS@
|
||||
aegisub_2_1_LDADD += libaudiovideo_ffmpegsource.a ../FFmpegSource2/libffmpegsource2_aegisub.a
|
||||
aegisub_2_1_LDFLAGS += @LIBPOSTPROC_LIBS@
|
||||
ffmpegsource = libffms
|
||||
endif
|
||||
|
||||
noinst_LIBRARIES += libsubtitle_provider.a
|
||||
libsubtitle_provider_a_SOURCES = subtitles_provider.cpp
|
||||
libsubtitle_provider_a_CPPFLAGS = @CSRI_CFLAGS@
|
||||
aegisub_2_1_LDADD += libsubtitle_provider.a
|
||||
SUBDIRS = \
|
||||
$(univchardet) \
|
||||
$(libass) \
|
||||
$(ffmpegsource) \
|
||||
src \
|
||||
automation \
|
||||
po \
|
||||
desktop
|
||||
|
||||
if WITH_LIBASS
|
||||
noinst_LIBRARIES += libsubtitle_ass.a
|
||||
libsubtitle_ass_a_SOURCES = subtitles_provider_libass.cpp
|
||||
libsubtitle_ass_a_CPPFLAGS = @LIBASS_CFLAGS@ @ICONV_CFLAGS@
|
||||
LIBS += @LIBASS_LIBS@
|
||||
aegisub_2_1_LDADD += libsubtitle_ass.a
|
||||
aegisub_2_1_LDFLAGS += @FONTCONFIG_LIBS@
|
||||
endif
|
||||
|
||||
if WITH_CSRI
|
||||
noinst_LIBRARIES += libsubtitle_csri.a
|
||||
libsubtitle_csri_a_SOURCES = subtitles_provider_csri.cpp
|
||||
libsubtitle_csri_a_CPPFLAGS = @ICONV_CFLAGS@ @CSRI_CFLAGS@
|
||||
LIBS += @CSRI_LIBS@
|
||||
aegisub_2_1_LDADD += libsubtitle_csri.a
|
||||
aegisub_2_1_LDFLAGS += @FONTCONFIG_LIBS@
|
||||
endif
|
||||
|
||||
if HAVE_AUTO4_LUA
|
||||
noinst_LIBRARIES += libauto4_lua.a
|
||||
libauto4_lua_a_SOURCES = auto4_lua.cpp auto4_lua_assfile.cpp auto4_lua_dialog.cpp auto4_lua_scriptreader.cpp
|
||||
libauto4_lua_a_CPPFLAGS = @LUA_CFLAGS@
|
||||
LIBS += @LUA_LDFLAGS@
|
||||
aegisub_2_1_LDADD += libauto4_lua.a
|
||||
endif
|
||||
|
||||
if HAVE_AUTO4_PERL
|
||||
noinst_LIBRARIES += libauto4_perl.a
|
||||
libauto4_perl_a_SOURCES = auto4_perl.cpp auto4_perl_script.cpp auto4_perl_dialogs.cpp auto4_perl_ass.cpp auto4_perl_console.cpp auto4_perl.h auto4_perl_console.h
|
||||
libauto4_perl_a_CPPFLAGS = @PERL_CFLAGS@
|
||||
LIBS += @PERL_LDFLAGS@
|
||||
aegisub_2_1_LDADD += libauto4_perl.a
|
||||
endif
|
||||
|
||||
if HAVE_AUTO4_RUBY
|
||||
noinst_LIBRARIES += libauto4_ruby.a
|
||||
libauto4_ruby_a_SOURCES = auto4_ruby_assfile.cpp auto4_ruby.cpp auto4_ruby_dialog.cpp
|
||||
libauto4_ruby_a_CPPFLAGS = @RUBY_CFLAGS@
|
||||
aegisub_2_1_LDFLAGS += @RUBY_LDFLAGS@
|
||||
aegisub_2_1_LDADD += libauto4_ruby.a
|
||||
endif
|
||||
|
||||
if BUILD_DARWIN
|
||||
AUTO3_LIB = ../auto3/.libs/libaegisub-auto3-2.1.0.dylib
|
||||
else
|
||||
AUTO3_LIB = ../auto3/.libs/libaegisub-auto3-2.1.so
|
||||
endif
|
||||
|
||||
if HAVE_AUTO3_LUA
|
||||
AUTO3_LUA = auto4_auto3.cpp
|
||||
# This is a workaround top stop libtool from binging in dependency_libs which
|
||||
# causes many link errors due to auto4 (lua 5.1) support.
|
||||
#aegisub_2_1_LDADD += -L$(srcdir)/../auto3 -laegisub-auto3-2.1
|
||||
aegisub_2_1_LDADD += ${AUTO3_LIB}
|
||||
endif
|
||||
|
||||
if HAVE_HUNSPELL
|
||||
noinst_LIBRARIES += libmisc_hunspell.a
|
||||
libmisc_hunspell_a_SOURCES = spellchecker_hunspell.cpp
|
||||
aegisub_2_1_LDFLAGS += @HUNSPELL_LIBS@
|
||||
aegisub_2_1_LDADD += libmisc_hunspell.a
|
||||
endif
|
||||
|
||||
if HAVE_UNIVCHARDET
|
||||
noinst_LIBRARIES += libmisc_universalchardet.a
|
||||
libmisc_universalchardet_a_SOURCES = charset_detect.cpp text_file_reader.cpp
|
||||
libmisc_universalchardet_a_CPPFLAGS = -D_X86_
|
||||
aegisub_2_1_LDADD += libmisc_universalchardet.a ../universalchardet/libuniversalchardet.a
|
||||
else
|
||||
UNIVCHARSET = charset_detect.cpp text_file_reader.cpp
|
||||
endif
|
||||
|
||||
if BUILD_DARWIN
|
||||
BEVEL_BUTTON = osx_bevelButton.cpp
|
||||
endif
|
||||
|
||||
if FONTLISTER_FREETYPE
|
||||
FONT_LISTER = font_file_lister_freetype.cpp
|
||||
endif
|
||||
|
||||
if FONTLISTER_FONTCONFIG
|
||||
FONT_LISTER = font_file_lister_fontconfig.cpp
|
||||
endif
|
||||
osx-bundle:
|
||||
@SHELL@ scripts/osx-bundle.sh "Aegisub (r@SVN_REVISION@-@DARWIN_ARCH@@PACKAGE_DEBUG@)" @AEGISUB_VERSION_DATA@ DICT_DIR=$(DICT_DIR)
|
||||
|
||||
|
||||
## These aren't built, but are listed here so 'make dist' can always find all the sources
|
||||
## This should also list all Win32 specific files
|
||||
EXTRA_aegisub_2_1_SOURCES = \
|
||||
font_file_lister.cpp \
|
||||
$(FONT_LISTER) \
|
||||
audio_player_dsound.cpp \
|
||||
audio_player_dsound2.cpp \
|
||||
audio_player_portaudio.cpp \
|
||||
audio_player_pulse.cpp \
|
||||
audio_provider_avs.cpp \
|
||||
audio_provider_lavc.cpp \
|
||||
auto4_auto3.cpp \
|
||||
auto4_lua.cpp \
|
||||
auto4_lua_assfile.cpp \
|
||||
auto4_lua_dialog.cpp \
|
||||
auto4_lua_scriptreader.cpp \
|
||||
auto4_ruby.cpp \
|
||||
auto4_ruby_assfile.cpp \
|
||||
auto4_ruby_dialog.cpp \
|
||||
avisynth_wrap.cpp \
|
||||
dialog_associations.cpp \
|
||||
lavc_file.cpp \
|
||||
lavc_keyframes.cpp \
|
||||
osx_bevelButton.cpp \
|
||||
setup.cpp \
|
||||
spellchecker_hunspell.cpp \
|
||||
stdwx.cpp \
|
||||
subtitle_format_prs.cpp \
|
||||
subtitles_provider_csri.cpp \
|
||||
subtitles_provider_libass.cpp \
|
||||
video_provider_avs.cpp \
|
||||
video_provider_dshow.cpp \
|
||||
video_provider_lavc.cpp
|
||||
osx-dmg:
|
||||
@SHELL@ scripts/osx-dmg.sh "Aegisub (r@SVN_REVISION@-@DARWIN_ARCH@@PACKAGE_DEBUG@)" "Aegisub (r@SVN_REVISION@-@DARWIN_ARCH@@PACKAGE_DEBUG@)"
|
||||
|
||||
osx-tinderbox-bundle:
|
||||
@SHELL@ scripts/osx-bundle.sh "$(T_BUNDLE)" @AEGISUB_VERSION_DATA@ DICT_DIR="$(DICT_DIR)"
|
||||
|
||||
aegisub_2_1_SOURCES = \
|
||||
$(AUTO3_LUA) \
|
||||
$(FFMPEG) \
|
||||
$(HUNSPELL) \
|
||||
$(LIBASS) \
|
||||
$(UNIVCHARSET) \
|
||||
$(BEVEL_BUTTON) \
|
||||
font_file_lister.cpp \
|
||||
$(FONT_LISTER) \
|
||||
MatroskaParser.c \
|
||||
aegisublocale.cpp \
|
||||
ass_attachment.cpp \
|
||||
ass_dialogue.cpp \
|
||||
ass_entry.cpp \
|
||||
ass_export_filter.cpp \
|
||||
ass_exporter.cpp \
|
||||
ass_file.cpp \
|
||||
ass_karaoke.cpp \
|
||||
ass_override.cpp \
|
||||
ass_style.cpp \
|
||||
ass_style_storage.cpp \
|
||||
ass_time.cpp \
|
||||
audio_box.cpp \
|
||||
audio_display.cpp \
|
||||
audio_karaoke.cpp \
|
||||
audio_player.cpp \
|
||||
audio_provider.cpp \
|
||||
audio_provider_convert.cpp \
|
||||
audio_provider_downmix.cpp \
|
||||
audio_provider_hd.cpp \
|
||||
audio_provider_pcm.cpp \
|
||||
audio_provider_ram.cpp \
|
||||
audio_provider_stream.cpp \
|
||||
audio_spectrum.cpp \
|
||||
auto4_base.cpp \
|
||||
avisynth_wrap.cpp \
|
||||
base_grid.cpp \
|
||||
browse_button.cpp \
|
||||
colorspace.cpp \
|
||||
colour_button.cpp \
|
||||
dialog_about.cpp \
|
||||
dialog_attachments.cpp \
|
||||
dialog_automation.cpp \
|
||||
dialog_colorpicker.cpp \
|
||||
dialog_detached_video.cpp \
|
||||
dialog_dummy_video.cpp \
|
||||
dialog_export.cpp \
|
||||
dialog_fonts_collector.cpp \
|
||||
dialog_jumpto.cpp \
|
||||
dialog_kanji_timer.cpp \
|
||||
dialog_options.cpp \
|
||||
dialog_paste_over.cpp \
|
||||
dialog_progress.cpp \
|
||||
dialog_properties.cpp \
|
||||
dialog_resample.cpp \
|
||||
dialog_search_replace.cpp \
|
||||
dialog_selection.cpp \
|
||||
dialog_shift_times.cpp \
|
||||
dialog_spellchecker.cpp \
|
||||
dialog_splash.cpp \
|
||||
dialog_style_editor.cpp \
|
||||
dialog_style_manager.cpp \
|
||||
dialog_styling_assistant.cpp \
|
||||
dialog_text_import.cpp \
|
||||
dialog_timing_processor.cpp \
|
||||
dialog_tip.cpp \
|
||||
dialog_translation.cpp \
|
||||
dialog_version_check.cpp \
|
||||
dialog_video_details.cpp \
|
||||
drop.cpp \
|
||||
export_clean_info.cpp \
|
||||
export_fixstyle.cpp \
|
||||
export_framerate.cpp \
|
||||
export_visible_lines.cpp \
|
||||
fft.cpp \
|
||||
frame_main.cpp \
|
||||
frame_main_events.cpp \
|
||||
gl_text.cpp \
|
||||
gl_wrap.cpp \
|
||||
help_button.cpp \
|
||||
hilimod_textctrl.cpp \
|
||||
hotkeys.cpp \
|
||||
idle_field_event.cpp \
|
||||
kana_table.cpp \
|
||||
keyframe.cpp \
|
||||
main.cpp \
|
||||
md5.c \
|
||||
mkv_wrap.cpp \
|
||||
mythes.cxx \
|
||||
options.cpp \
|
||||
plugin_manager.cpp \
|
||||
scintilla_text_ctrl.cpp \
|
||||
spellchecker.cpp \
|
||||
spline.cpp \
|
||||
spline_curve.cpp \
|
||||
standard_paths.cpp \
|
||||
static_bmp.cpp \
|
||||
string_codec.cpp \
|
||||
subs_edit_box.cpp \
|
||||
subs_edit_ctrl.cpp \
|
||||
subs_grid.cpp \
|
||||
subs_preview.cpp \
|
||||
subtitle_format.cpp \
|
||||
subtitle_format_ass.cpp \
|
||||
subtitle_format_dvd.cpp \
|
||||
subtitle_format_encore.cpp \
|
||||
subtitle_format_microdvd.cpp \
|
||||
subtitle_format_mkv.cpp \
|
||||
subtitle_format_srt.cpp \
|
||||
subtitle_format_transtation.cpp \
|
||||
subtitle_format_ttxt.cpp \
|
||||
subtitle_format_txt.cpp \
|
||||
text_file_writer.cpp \
|
||||
thesaurus.cpp \
|
||||
thesaurus_myspell.cpp \
|
||||
timeedit_ctrl.cpp \
|
||||
toggle_bitmap.cpp \
|
||||
tooltip_manager.cpp \
|
||||
utils.cpp \
|
||||
validators.cpp \
|
||||
variable_data.cpp \
|
||||
vector2d.cpp \
|
||||
version.cpp \
|
||||
vfr.cpp \
|
||||
video_box.cpp \
|
||||
video_context.cpp \
|
||||
video_display.cpp \
|
||||
video_frame.cpp \
|
||||
video_provider_cache.cpp \
|
||||
video_provider_dummy.cpp \
|
||||
video_provider_manager.cpp \
|
||||
video_slider.cpp \
|
||||
visual_feature.cpp \
|
||||
visual_tool.cpp \
|
||||
visual_tool_clip.cpp \
|
||||
visual_tool_cross.cpp \
|
||||
visual_tool_drag.cpp \
|
||||
visual_tool_rotatexy.cpp \
|
||||
visual_tool_rotatez.cpp \
|
||||
visual_tool_scale.cpp \
|
||||
visual_tool_vector_clip.cpp
|
||||
|
||||
|
||||
aegisub_2_1_SOURCES += \
|
||||
$(srcdir)/*.h \
|
||||
$(srcdir)/include/aegisub/*.h \
|
||||
$(srcdir)/config/*.h
|
||||
|
||||
noinst_HEADERS = \
|
||||
$(srcdir)/boost/*.hpp \
|
||||
gl/glext.h \
|
||||
$(srcdir)/*.hxx \
|
||||
osx_bevelButton.h
|
||||
osx-tinderbox-dmg:
|
||||
@SHELL@ scripts/osx-dmg.sh "$(T_DMG)" "$(T_BUNDLE)"
|
||||
mv "$(T_DMG).dmg" bundle.dmg
|
||||
|
||||
EXTRA_DIST = \
|
||||
auto4_perldata.inc
|
||||
intltool-extract.in \
|
||||
intltool-merge.in \
|
||||
intltool-update.in \
|
||||
config.guess \
|
||||
config.sub \
|
||||
depcomp \
|
||||
install-sh \
|
||||
missing \
|
||||
INSTALL \
|
||||
README \
|
||||
svn_revision
|
||||
|
||||
# m4macros/
|
||||
EXTRA_DIST += \
|
||||
m4macros/check_gnu_make.m4 \
|
||||
m4macros/acx_pthread.m4 \
|
||||
m4macros/ac_agi.m4 \
|
||||
m4macros/ax_lang_compiler_ms.m4 \
|
||||
m4macros/ac_agi_mdcpucfg.m4 \
|
||||
m4macros/ax_openmp.m4 \
|
||||
m4macros/ax_check_gl.m4 \
|
||||
m4macros/ac_flag.m4
|
||||
|
||||
# scripts/
|
||||
EXTRA_DIST += \
|
||||
scripts/osx-bundle-restart-helper.c \
|
||||
scripts/osx-bundle.sh \
|
||||
scripts/osx-dmg-dsstore.pl \
|
||||
scripts/osx-dmg.sh \
|
||||
scripts/osx-fix-libs.py \
|
||||
scripts/unix-gen-res.awk \
|
||||
scripts/unix-gen-xpm.awk
|
||||
|
||||
# packages/osx_bundle/
|
||||
EXTRA_DIST += \
|
||||
packages/osx_bundle/Contents/Info.plist \
|
||||
packages/osx_bundle/Contents/Resources/*.icns \
|
||||
packages/osx_bundle/Contents/Resources/etc/fonts/fonts.conf \
|
||||
packages/osx_bundle/Contents/Resources/etc/fonts/fonts.dtd \
|
||||
packages/osx_bundle/Contents/Resources/etc/fonts/conf.d/*.conf
|
||||
|
||||
# packages/osx_dmg/
|
||||
EXTRA_DIST += \
|
||||
packages/osx_dmg/dmg_background.png \
|
||||
packages/osx_dmg/dmg_set_style.applescript
|
||||
|
||||
|
||||
CLEANFILES= \
|
||||
intltool-extract \
|
||||
intltool-merge \
|
||||
intltool-update
|
||||
|
|
|
@ -34,7 +34,7 @@ test -z "$srcdir" && srcdir=.
|
|||
ORIGDIR=`pwd`
|
||||
cd $srcdir
|
||||
|
||||
test -d aegisub || {
|
||||
test -d src || {
|
||||
echo
|
||||
echo "You must run this script in the top-level $PROJECT directory."
|
||||
echo
|
||||
|
@ -188,7 +188,7 @@ if test -z "$BIN_CONVERT"; then
|
|||
fi
|
||||
|
||||
$ECHO_N "checking for ImageMagick 'convert' utility ... "
|
||||
if test -x "$BIN_CONVERT"; then
|
||||
if test -x "$BIN_CONVERT" && $BIN_CONVERT --version |grep -c ImageMagick > /dev/null; then
|
||||
echo $BIN_CONVERT
|
||||
else
|
||||
echo "not found"
|
||||
|
@ -225,21 +225,22 @@ if test "$DIE" -eq 1; then
|
|||
fi
|
||||
|
||||
|
||||
echo
|
||||
echo "I am going to run ./configure with the following arguments:"
|
||||
echo
|
||||
echo " --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS $@"
|
||||
echo
|
||||
if ! test "$1" = "--skip-configure"; then
|
||||
echo
|
||||
echo "I am going to run ./configure with the following arguments:"
|
||||
echo
|
||||
echo " --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS $@"
|
||||
echo
|
||||
|
||||
if test -z "$*"; then
|
||||
echo "If you wish to pass additional arguments, please specify them "
|
||||
echo "on the $0 command line or set the AUTOGEN_CONFIGURE_ARGS "
|
||||
echo "environment variable."
|
||||
echo
|
||||
if test -z "$*"; then
|
||||
echo "If you wish to pass additional arguments, please specify them "
|
||||
echo "on the $0 command line or set the AUTOGEN_CONFIGURE_ARGS "
|
||||
echo "environment variable."
|
||||
echo
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
echo "--- Checking for required M4 files ---"
|
||||
|
||||
if test -z "$ACLOCAL_FLAGS"; then
|
||||
|
@ -282,19 +283,19 @@ fi
|
|||
|
||||
echo "--- Converting BMP resource files -> XPM ---"
|
||||
# BMP -> XPM via src/res.rc
|
||||
cat ${srcdir}/aegisub/res.rc | ${BIN_AWK} -f ${srcdir}/scripts/unix-gen-xpm.awk BIN_CONVERT="$BIN_CONVERT" > ${srcdir}/aegisub/bitmaps/Makefile.bitmaps
|
||||
cd ${srcdir}/aegisub/bitmaps
|
||||
cat ${srcdir}/src/res.rc | ${BIN_AWK} -f ${srcdir}/scripts/unix-gen-xpm.awk BIN_CONVERT="$BIN_CONVERT" > ${srcdir}/src/bitmaps/Makefile.bitmaps
|
||||
cd ${srcdir}/src/bitmaps
|
||||
make -f Makefile.bitmaps
|
||||
cd ${srcdir}
|
||||
|
||||
$BIN_AWK '/BITMAP/ { image[count] = $1; ++count} END { printf("EXTRA_DIST="); for (v in image) printf(" \\\n %s_xpm.xpm", image[v])}' \
|
||||
${srcdir}/aegisub/res.rc \
|
||||
> ${srcdir}/aegisub/bitmaps/Makefile.am
|
||||
${srcdir}/src/res.rc \
|
||||
> ${srcdir}/src/bitmaps/Makefile.am
|
||||
|
||||
echo "--- Generating libresrc/resrc.cpp, libresrc/libresrc.h from res.rc ---"
|
||||
$BIN_AWK -f scripts/unix-gen-res.awk ${srcdir}/aegisub/res.rc \
|
||||
RESRC_CPP="${srcdir}/aegisub/libresrc/resrc.cpp" \
|
||||
RESRC_H="${srcdir}/aegisub/libresrc/libresrc.h"
|
||||
$BIN_AWK -f scripts/unix-gen-res.awk ${srcdir}/src/res.rc \
|
||||
RESRC_CPP="${srcdir}/src/libresrc/resrc.cpp" \
|
||||
RESRC_H="${srcdir}/src/libresrc/libresrc.h"
|
||||
|
||||
rm -rf autom4te.cache
|
||||
|
||||
|
@ -321,29 +322,34 @@ $AUTOCONF || exit $?
|
|||
echo "--- $GETTEXTIZE ---"
|
||||
$GETTEXTIZE --force || exit $?
|
||||
|
||||
echo "--- $INTTOOLIZE ---"
|
||||
echo "--- $INTLTOOLIZE ---"
|
||||
$INTLTOOLIZE --force --automake || exit $?
|
||||
|
||||
cd $ORIGDIR
|
||||
|
||||
echo "--- $srcdir/configure ---"
|
||||
$srcdir/configure --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS "$@"
|
||||
RC=$?
|
||||
|
||||
echo
|
||||
echo
|
||||
echo "***********************************************************************"
|
||||
echo "*"
|
||||
echo "* Please do not ask for support when using the SVN verison of aegisub,"
|
||||
echo "* download an official distfile in order to receive support."
|
||||
echo "*"
|
||||
echo "***********************************************************************"
|
||||
echo
|
||||
|
||||
if test $RC -ne 0; then
|
||||
if ! test "$1" = "--skip-configure"; then
|
||||
echo "--- $srcdir/configure ---"
|
||||
echo "Running '$srcdir/configure --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS "$@"'"
|
||||
echo
|
||||
echo "Configure failed or did not finish!"
|
||||
exit $RC
|
||||
$srcdir/configure --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS "$@"
|
||||
RC=$?
|
||||
|
||||
echo
|
||||
echo
|
||||
echo "***********************************************************************"
|
||||
echo "*"
|
||||
echo "* Please do not ask for support when using the SVN verison of aegisub,"
|
||||
echo "* download an official distfile in order to receive support."
|
||||
echo "*"
|
||||
echo "***********************************************************************"
|
||||
echo
|
||||
|
||||
if test $RC -ne 0; then
|
||||
echo
|
||||
echo "Configure failed or did not finish!"
|
||||
exit $RC
|
||||
fi
|
||||
|
||||
echo "Now type 'make' to compile $PROJECT."
|
||||
fi
|
||||
|
||||
echo "Now type 'make' to compile $PROJECT."
|
|
@ -8,7 +8,7 @@ writing) @ http://www.malakith.net/aegiwiki
|
|||
mechanism works. Even so, I am not resposible if it damages your subtitles permanently, so please
|
||||
back up your subtitle file before applying the cleaning up
|
||||
|
||||
Copyright (c) 2007 ai-chan (Aegisub's forum member and registered nick holder of Rizon irc network)
|
||||
Copyright (c) 2007-2009 Muhammad Lukman Nasaruddin (aka ai-chan, Aegisub's forum member)
|
||||
|
||||
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,
|
||||
|
@ -28,9 +28,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
script_name = "Clean Tags"
|
||||
script_description = "Clean subtitle lines by re-arranging ASS tags and override blocks within the lines"
|
||||
script_author = "ai-chan"
|
||||
script_version = "1.150"
|
||||
script_modified = "12 September 2007"
|
||||
script_author = "Muhammad Lukman Nasaruddin (ai-chan)"
|
||||
script_version = "1.20"
|
||||
script_modified = "25 February 2009"
|
||||
|
||||
include("cleantags.lua")
|
||||
|
||||
|
@ -38,11 +38,11 @@ function cleantags_subs(subtitles)
|
|||
local linescleaned = 0
|
||||
for i = 1, #subtitles do
|
||||
aegisub.progress.set(i * 100 / #subtitles)
|
||||
if subtitles[i].class == "dialogue" and subtitles[i].text ~= "" then
|
||||
if subtitles[i].class == "dialogue" and not subtitles[i].comment and subtitles[i].text ~= "" then
|
||||
ntext = cleantags(subtitles[i].text)
|
||||
local nline = subtitles[i]
|
||||
nline.text = ntext
|
||||
subtitles[i] = nline -- I don't understand why we need these steps to incorporate new text
|
||||
subtitles[i] = nline
|
||||
linescleaned = linescleaned + 1
|
||||
aegisub.progress.task(linescleaned .. " lines cleaned")
|
||||
end
|
|
@ -34,7 +34,7 @@
|
|||
script_name = "Karaoke Templater"
|
||||
script_description = "Macro and export filter to apply karaoke effects using the template language"
|
||||
script_author = "Niels Martin Hansen"
|
||||
script_version = "2.1.0"
|
||||
script_version = "2.1.7"
|
||||
|
||||
|
||||
include("karaskel.lua")
|
||||
|
@ -265,6 +265,26 @@ function matching_templates(templates, line, tenv)
|
|||
return test_next
|
||||
end
|
||||
|
||||
-- Iterator function, run a loop using tenv.j and tenv.maxj as loop controllers
|
||||
function template_loop(tenv, initmaxj)
|
||||
local oldmaxj = initmaxj
|
||||
tenv.maxj = initmaxj
|
||||
tenv.j = 0
|
||||
local function itor()
|
||||
if tenv.j >= tenv.maxj or aegisub.progress.is_cancelled() then
|
||||
return nil
|
||||
else
|
||||
tenv.j = tenv.j + 1
|
||||
if oldmaxj ~= tenv.maxj then
|
||||
aegisub.debug.out(5, "Number of loop iterations changed from %d to %d\n", oldmaxj, tenv.maxj)
|
||||
oldmaxj = tenv.maxj
|
||||
end
|
||||
return tenv.j, tenv.maxj
|
||||
end
|
||||
end
|
||||
return itor
|
||||
end
|
||||
|
||||
|
||||
-- Apply the templates
|
||||
function apply_templates(meta, styles, subs, templates)
|
||||
|
@ -278,6 +298,8 @@ function apply_templates(meta, styles, subs, templates)
|
|||
}
|
||||
tenv.tenv = tenv
|
||||
|
||||
-- Define helper functions in tenv
|
||||
|
||||
tenv.retime = function(mode, addstart, addend)
|
||||
local line, syl = tenv.line, tenv.syl
|
||||
local newstart, newend = line.start_time, line.end_time
|
||||
|
@ -322,8 +344,31 @@ function apply_templates(meta, styles, subs, templates)
|
|||
line.duration = newend - newstart
|
||||
return ""
|
||||
end
|
||||
|
||||
tenv.fxgroup = {}
|
||||
|
||||
tenv.relayer = function(layer)
|
||||
line.layer = layer
|
||||
return ""
|
||||
end
|
||||
|
||||
tenv.restyle = function(style)
|
||||
line.style = style
|
||||
line.styleref = styles[style]
|
||||
return ""
|
||||
end
|
||||
|
||||
tenv.maxloop = function(newmaxj)
|
||||
tenv.maxj = newmaxj
|
||||
return ""
|
||||
end
|
||||
tenv.maxloops = tenv.maxloop
|
||||
tenv.loopctl = function(newj, newmaxj)
|
||||
tenv.j = newj
|
||||
tenv.maxj = newmaxj
|
||||
return ""
|
||||
end
|
||||
|
||||
-- run all run-once code snippets
|
||||
for k, t in pairs(templates.once) do
|
||||
assert(t.code, "WTF, a 'once' template without code?")
|
||||
|
@ -443,8 +488,7 @@ function apply_line(meta, styles, subs, line, templates, tenv)
|
|||
-- Apply all line templates
|
||||
aegisub.debug.out(5, "Running line templates\n")
|
||||
for t in matching_templates(templates.line, line, tenv) do
|
||||
tenv.j = 0
|
||||
tenv.maxj = t.loops
|
||||
if aegisub.progress.is_cancelled() then break end
|
||||
|
||||
-- Set varctx for per-line variables
|
||||
varctx["start"] = varctx.lstart
|
||||
|
@ -464,11 +508,15 @@ function apply_line(meta, styles, subs, line, templates, tenv)
|
|||
varctx.x = varctx.lx
|
||||
varctx.y = varctx.ly
|
||||
|
||||
while tenv.j < t.loops do
|
||||
tenv.j = tenv.j + 1
|
||||
for j, maxj in template_loop(tenv, t.loops) do
|
||||
if t.code then
|
||||
aegisub.debug.out(5, "Code template, %s\n", t.code)
|
||||
tenv.line = line
|
||||
-- Although run_code_template also performs template looping this works
|
||||
-- by "luck", since by the time the first loop of this outer loop completes
|
||||
-- the one run by run_code_template has already performed all iterations
|
||||
-- and has tenv.j and tenv.maxj in a loop-ending state, causing the outer
|
||||
-- loop to only ever run once.
|
||||
run_code_template(t, tenv)
|
||||
else
|
||||
aegisub.debug.out(5, "Line template, pre = '%s', t = '%s'\n", t.pre, t.t)
|
||||
|
@ -512,6 +560,7 @@ function apply_line(meta, styles, subs, line, templates, tenv)
|
|||
|
||||
-- Loop over syllables
|
||||
for i = 0, line.kara.n do
|
||||
if aegisub.progress.is_cancelled() then break end
|
||||
local syl = line.kara[i]
|
||||
|
||||
aegisub.debug.out(5, "Applying templates to syllable: %s\n", syl.text)
|
||||
|
@ -522,6 +571,7 @@ function apply_line(meta, styles, subs, line, templates, tenv)
|
|||
|
||||
-- Loop over furigana
|
||||
for i = 1, line.furi.n do
|
||||
if aegisub.progress.is_cancelled() then break end
|
||||
local furi = line.furi[i]
|
||||
|
||||
aegisub.debug.out(5, "Applying templates to furigana: %s\n", furi.text)
|
||||
|
@ -540,9 +590,7 @@ function run_code_template(template, tenv)
|
|||
else
|
||||
local pcall = pcall
|
||||
setfenv(f, tenv)
|
||||
tenv.maxj = template.loops
|
||||
for j = 1, template.loops do
|
||||
tenv.j = j
|
||||
for j, maxj in template_loop(tenv, template.loops) do
|
||||
local res, err = pcall(f)
|
||||
if not res then
|
||||
aegisub.debug.out(2, "Runtime error in template code: %s\nCode producing error: %s\n\n", err, template.code)
|
||||
|
@ -600,20 +648,20 @@ function run_text_template(template, tenv, varctx)
|
|||
end
|
||||
|
||||
function apply_syllable_templates(syl, line, templates, tenv, varctx, subs)
|
||||
local applied_templates = false
|
||||
local applied = 0
|
||||
|
||||
-- Loop over all templates matching the line style
|
||||
for t in matching_templates(templates, line, tenv) do
|
||||
if aegisub.progress.is_cancelled() then break end
|
||||
|
||||
tenv.syl = syl
|
||||
tenv.basesyl = syl
|
||||
set_ctx_syl(varctx, line, syl)
|
||||
|
||||
if apply_one_syllable_template(syl, line, t, tenv, varctx, subs, false, false) then
|
||||
applied_templates = true
|
||||
end
|
||||
applied = applied + apply_one_syllable_template(syl, line, t, tenv, varctx, subs, false, false)
|
||||
end
|
||||
|
||||
return applied_templates
|
||||
return applied > 0
|
||||
end
|
||||
|
||||
function is_syl_blank(syl)
|
||||
|
@ -630,19 +678,21 @@ function is_syl_blank(syl)
|
|||
end
|
||||
|
||||
function apply_one_syllable_template(syl, line, template, tenv, varctx, subs, skip_perchar, skip_multi)
|
||||
if aegisub.progress.is_cancelled() then return 0 end
|
||||
local t = template
|
||||
local applied = 0
|
||||
|
||||
aegisub.debug.out(5, "Applying template to one syllable with text: %s\n", syl.text)
|
||||
|
||||
-- Check for right inline_fx
|
||||
if t.fx and t.fx ~= syl.inline_fx then
|
||||
aegisub.debug.out(5, "Syllable has wrong inline-fx (wanted '%s', got '%s'), skipping.\n", t.inline_fx, syl.inline_fx)
|
||||
return false
|
||||
return 0
|
||||
end
|
||||
|
||||
if t.noblank and is_syl_blank(syl) then
|
||||
aegisub.debug.out(5, "Syllable is blank, skipping.\n")
|
||||
return false
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Recurse to per-char if required
|
||||
|
@ -665,10 +715,10 @@ function apply_one_syllable_template(syl, line, template, tenv, varctx, subs, sk
|
|||
left = left + width
|
||||
set_ctx_syl(varctx, line, charsyl)
|
||||
|
||||
apply_one_syllable_template(charsyl, line, t, tenv, varctx, subs, true, false)
|
||||
applied = applied + apply_one_syllable_template(charsyl, line, t, tenv, varctx, subs, true, false)
|
||||
end
|
||||
|
||||
return true
|
||||
return applied
|
||||
end
|
||||
|
||||
-- Recurse to multi-hl if required
|
||||
|
@ -684,10 +734,10 @@ function apply_one_syllable_template(syl, line, template, tenv, varctx, subs, sk
|
|||
hlsyl.duration = hldata.duration
|
||||
set_ctx_syl(varctx, line, hlsyl)
|
||||
|
||||
apply_one_syllable_template(hlsyl, line, t, tenv, varctx, subs, true, true)
|
||||
applied = applied + apply_one_syllable_template(hlsyl, line, t, tenv, varctx, subs, true, true)
|
||||
end
|
||||
|
||||
return true
|
||||
return applied
|
||||
end
|
||||
|
||||
-- Regular processing
|
||||
|
@ -697,9 +747,7 @@ function apply_one_syllable_template(syl, line, template, tenv, varctx, subs, sk
|
|||
run_code_template(t, tenv)
|
||||
else
|
||||
aegisub.debug.out(5, "Running %d effect loops\n", t.loops)
|
||||
tenv.maxj = t.loops
|
||||
for j = 1, t.loops do
|
||||
tenv.j = j
|
||||
for j, maxj in template_loop(tenv, t.loops) do
|
||||
local newline = table.copy(line)
|
||||
newline.styleref = syl.style
|
||||
newline.style = syl.style.name
|
||||
|
@ -714,10 +762,11 @@ function apply_one_syllable_template(syl, line, template, tenv, varctx, subs, sk
|
|||
newline.effect = "fx"
|
||||
aegisub.debug.out(5, "Generated line with text: %s\n", newline.text)
|
||||
subs.append(newline)
|
||||
applied = applied + 1
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
return applied
|
||||
end
|
||||
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
--[[
|
||||
"Clean Tags" -- An Auto4 LUA script for cleaning up ASS subtitle lines of badly-formed override
|
||||
blocks and redundant/duplicate tags
|
||||
* Designed to work for Aegisub 2.0 and above (only pre-release version was available at the time of
|
||||
writing) @ http://www.malakith.net/aegiwiki
|
||||
* Designed to work for Aegisub 2.0 and above
|
||||
* include()'ed this file from any auto4 script to use the cleantags() function below
|
||||
* Might change from time to time so look out for cleantags_version below
|
||||
|
||||
Copyright (c) 2007 ai-chan (Aegisub's forum member and registered nick holder of Rizon irc network)
|
||||
Copyright (c) 2007-2009 Muhammad Lukman Nasaruddin (aka ai-chan, Aegisub's forum member)
|
||||
|
||||
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,
|
||||
|
@ -24,8 +23,8 @@ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
cleantags_version = "1.200"
|
||||
cleantags_modified = "13 September 2007"
|
||||
cleantags_version = "1.300"
|
||||
cleantags_modified = "27 February 2009"
|
||||
|
||||
ktag = "\\[kK][fo]?%d+"
|
||||
|
||||
|
@ -34,13 +33,13 @@ Takes: text
|
|||
Returns: cleaned up text
|
||||
]]
|
||||
function cleantags(text)
|
||||
--[[ Combine adjacentext override override blocks into one ]]
|
||||
--[[ Combine adjacent override override blocks into one ]]
|
||||
function combineadjacentnotks(block1, block2)
|
||||
if string.find(block1, ktag) and string.find(block2, ktag) then
|
||||
-- if both adjacentext override blocks have \k , letext them be
|
||||
-- if both adjacent override blocks have \k , let them be
|
||||
return "{" .. block1 .. "}" .. string.char(1) .. "{" .. block2 .. "}" -- char(1) prevents infinite loop
|
||||
else
|
||||
-- either one or both override blocks don'text have \k , so combine them into one override block
|
||||
-- either one or both override blocks don't have \k , so combine them into one override block
|
||||
return "{" .. block1 .. block2 .. "}"
|
||||
end
|
||||
end
|
||||
|
@ -50,17 +49,17 @@ function cleantags(text)
|
|||
until replaced == 0
|
||||
text = string.gsub(text, string.char(1), "") -- removes all char(1) we inserted
|
||||
|
||||
--[[ Move firstext \k tag in override blocks to the frontext ]]
|
||||
--[[ Move first \k tag in override blocks to the front ]]
|
||||
text = string.gsub(text, "{([^{}]-)(" .. ktag .. ")(.-)}", "{%2%1%3}")
|
||||
|
||||
--[[ For some reasons if one override block has more than one \k tag,
|
||||
push those to behind the firstext \k tag (which has been pushed to frontext already) ]]
|
||||
push those to behind the first \k tag (which has been pushed to front already) ]]
|
||||
repeat
|
||||
if aegisub.progress.is_cancelled() then return end
|
||||
text, replaced = string.gsub(text, "{([^{}]-)(" .. ktag .. ")(\\[^kK][^}]-)(" .. ktag .. ")(.-)}", "{%1%2%4%3%5}")
|
||||
until replaced == 0
|
||||
|
||||
--[[ Move to the frontext all tags thatext affectext the whole line (i.e. notext affected by their positions in the line) ]]
|
||||
--[[ Move to the front all tags that affect the whole line (i.e. not affected by their positions in the line) ]]
|
||||
local linetags = ""
|
||||
function first(pattern)
|
||||
local p_s, _, p_tag = string.find(text, pattern)
|
||||
|
@ -84,26 +83,29 @@ function cleantags(text)
|
|||
first("(\\an?%d+)")
|
||||
-- \org
|
||||
first("(\\org%([^,%)]*,[^,%)]*%))")
|
||||
-- \move and \pos (the firstext one wins)
|
||||
-- \move and \pos (the first one wins)
|
||||
firstoftwo("(\\move%([^,%)]*,[^,%)]*,[^,%)]*,[^,%)]*%))", "(\\pos%([^,%)]*,[^,%)]*%))")
|
||||
-- \fade and \fad (the firstext one wins)
|
||||
-- \fade and \fad (the first one wins)
|
||||
firstoftwo("(\\fade%([^,%)]*,[^,%)]*,[^,%)]*,[^,%)]*,[^,%)]*,[^,%)]*,[^,%)]*%))", "(\\fad%([^,%)]*,[^,%)]*%))")
|
||||
-- integrate
|
||||
if string.len(linetags) > 0 then
|
||||
if string.sub(text, 1, 1) == "{" then
|
||||
text = "{" .. linetags .. string.sub(text, 2)
|
||||
else
|
||||
text = "{" .. linetags .. "}" .. t
|
||||
text = "{" .. linetags .. "}" .. text
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Remove any spaces within parenteses within override blocks ]]
|
||||
--[[ (removed in v 1.2)
|
||||
repeat
|
||||
if aegisub.progress.is_cancelled() then return end
|
||||
text, replaced2 = string.gsub(text, "({[^}]*%([^%s%)}]*,)%s+(.*%)[^}]*})", "%1%2")
|
||||
until replaced2 == 0 ]]
|
||||
|
||||
--[[ Remove any spaces within parenteses within override blocks except for \clip tags ]]
|
||||
local comb = function(a,b,c,d,e)
|
||||
if c ~= "\\clip" or d:sub(-1):find("[,%({]") or e:sub(1,1):find("[,%)}]") then return a..b..d..e
|
||||
else return a..b..d..string.char(2)..e end
|
||||
end
|
||||
repeat
|
||||
text, replaced2 = string.gsub(text, "({[^}\\]*)([^}%s]*(\\[^%(}\\%s]*))%s*(%([^%s%)}]*)%s+([^}]*)", comb)
|
||||
until replaced2 == 0
|
||||
text, _ = text:gsub(string.char(2)," ")
|
||||
|
||||
--[[ Remove all empty override blocks ==> {} ]]
|
||||
text = string.gsub(text, "{%s*}", "")
|
||||
|
|
@ -12,7 +12,28 @@ function test7(subtitles, selected_lines, active_line)
|
|||
local a, b = aegisub.dialog.display({{class="label", label="Test..."}}, {})
|
||||
report_dialog_result(a, b)
|
||||
aegisub.progress.set(50)
|
||||
a, b = aegisub.dialog.display({{class="edit", name="foo", text=""}}, {"foo", "bar"})
|
||||
a, b = aegisub.dialog.display(
|
||||
{
|
||||
{class="edit", name="foo", text="", x=0, y=0},
|
||||
{class="intedit", name="e1", value=20, x=0, y=1},
|
||||
{class="intedit", name="e2", value=30, min=10, max=50, x=1, y=1},
|
||||
{class="floatedit", name="e3", value=19.95, x=0, y=2},
|
||||
{class="floatedit", name="e4", value=123.63423, min=-4.3, max=2091, x=1, y=2},
|
||||
{class="floatedit", name="e5", value=-4, step=0.21, x=2, y=2},
|
||||
{class="floatedit", name="e6", value=22, min=0, max=100, step=1.4, x=3, y=2},
|
||||
{class="textbox", name="e7", text="hmm wuzzis say?", x=0, y=3, width=4},
|
||||
{class="dropdown", name="l1", items={"abc", "def", "ghi"}, value="def", x=0, y=4},
|
||||
{class="dropdown", name="l2", items={"abc", "def", "ghi"}, value="doesnotexist", x=1, y=4},
|
||||
{class="checkbox", name="b1", value=true, label='checked', x=0, y=5},
|
||||
{class="checkbox", name="b2", value=false, label='cleared', x=1, y=5},
|
||||
{class="color", name="c1", value="#00ff11", x=0, y=6},
|
||||
{class="color", name="c2", value="&H0011ff00", x=1, y=6},
|
||||
{class="coloralpha", name="c3", value="#aabbccdd", x=0, y=7},
|
||||
{class="coloralpha", name="c4", value="&Hddccbbaa&", x=1, y=7},
|
||||
{class="alpha", name="c5", value="#12", x=0, y=8},
|
||||
{class="alpha", name="c6", value="&H12&", x=1, y=8}
|
||||
},
|
||||
{"foo", "bar"})
|
||||
report_dialog_result(a, b)
|
||||
end
|
||||
|
||||
|
@ -26,7 +47,8 @@ function report_dialog_result(button, controls)
|
|||
aegisub.debug.out("clicked '" .. button .. "'\n")
|
||||
end
|
||||
for key, val in pairs(controls) do
|
||||
aegisub.debug.out(key .. ': ' .. val .. '\n')
|
||||
local printable = (val == true and "true") or (val == false and "false") or tostring(val)
|
||||
aegisub.debug.out("%s: %s\n", key, printable)
|
||||
end
|
||||
aegisub.debug.out(" - - - - -\n")
|
||||
end
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue