Add MP3 audio extraction
This commit is contained in:
parent
7b81712ef8
commit
fa58baceda
100
src/AudioStream.php
Normal file
100
src/AudioStream.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace swf2ass;
|
||||
|
||||
class AudioStream{
|
||||
const FORMAT_UNCOMPRESSED_NATIVE_ENDIAN = 0;
|
||||
const FORMAT_UNCOMPRESSED_LITTLE_ENDIAN = 3;
|
||||
|
||||
const FORMAT_ADPCM = 1;
|
||||
|
||||
const FORMAT_MP3 = 2;
|
||||
|
||||
const FORMAT_NELLYMOSER_16 = 4;
|
||||
const FORMAT_NELLYMOSER_8 = 5;
|
||||
const FORMAT_NELLYMOSER = 6;
|
||||
|
||||
const FORMAT_SPEEX = 11;
|
||||
|
||||
|
||||
private int $format;
|
||||
private int $sampleRate;
|
||||
private int $sampleSize;
|
||||
private int $channels;
|
||||
|
||||
private ?int $start = null;
|
||||
|
||||
|
||||
private array $frames = [];
|
||||
|
||||
public function __construct(int $format, int $sampleRate, int $sampleSize, int $channels){
|
||||
$this->format = $format;
|
||||
$this->sampleRate = $sampleRate;
|
||||
$this->sampleSize = $sampleSize;
|
||||
$this->channels = $channels;
|
||||
}
|
||||
|
||||
public function getFormat() : int{
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
public function setStartFrame(?int $start){
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
public function getStartFrame() : ?int{
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
public function addStreamBlock(array $node){
|
||||
if($node["tagType"] !== "SoundStreamBlock"){
|
||||
throw new \Exception("Invalid tag ". $node["tagType"] );
|
||||
}
|
||||
|
||||
switch ($this->format){
|
||||
case self::FORMAT_MP3:
|
||||
$this->addMP3SoundData($node["soundStreamData"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function addMP3SoundData(string $bin){
|
||||
$offset = 0;
|
||||
[,$sampleCount] = unpack("v", $bin, $offset);
|
||||
$offset += 2;
|
||||
[,$seekSamples] = unpack("s", $bin, $offset);
|
||||
$offset += 2;
|
||||
|
||||
$frameSize = $this->sampleRate <= 11025 ? 576 : 1152;
|
||||
$this->frames[] = substr($bin, $offset);
|
||||
}
|
||||
|
||||
public function getAudioData() : string{
|
||||
//TODO: header?
|
||||
return implode("", $this->frames);
|
||||
}
|
||||
|
||||
public static function fromSoundStreamHeadTag(array $node) : ?AudioStream{
|
||||
if(!isset($node["streamSoundCompression"]) or !isset($node["streamSoundRate"]) or !isset($node["streamSoundSize"]) or !isset($node["streamSoundType"])){
|
||||
return null;
|
||||
}
|
||||
|
||||
$rate = null;
|
||||
switch ($node["streamSoundRate"]){
|
||||
case 0:
|
||||
$rate = 5512;
|
||||
break;
|
||||
case 1:
|
||||
$rate = 11025;
|
||||
break;
|
||||
case 2:
|
||||
$rate = 22050;
|
||||
break;
|
||||
case 3:
|
||||
$rate = 44100;
|
||||
break;
|
||||
}
|
||||
|
||||
return new AudioStream($node["streamSoundCompression"], $rate, $node["streamSoundSize"] === 1 ? 16 : 8, $node["streamSoundType"] === 1 ? 2 : 1);
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ class SWFProcessor extends SWFTreeProcessor {
|
|||
|
||||
private float $frameRate;
|
||||
|
||||
private $audio = null;
|
||||
private ?AudioStream $audio = null;
|
||||
|
||||
private Rectangle $viewPort;
|
||||
|
||||
|
@ -51,7 +51,7 @@ class SWFProcessor extends SWFTreeProcessor {
|
|||
return $this->viewPort;
|
||||
}
|
||||
|
||||
public function getAudio() {
|
||||
public function getAudio() : ?AudioStream {
|
||||
return $this->audio;
|
||||
}
|
||||
|
||||
|
@ -68,18 +68,17 @@ class SWFProcessor extends SWFTreeProcessor {
|
|||
return $node["tagType"];
|
||||
case "SoundStreamHead":
|
||||
case "SoundStreamHead2":
|
||||
$this->audio = (object)["node" => $node, "start" => null, "content" => [],];
|
||||
$this->audio = AudioStream::fromSoundStreamHeadTag($node);
|
||||
return $node["tagType"];
|
||||
case "DefineSound":
|
||||
$this->audio = (object)["node" => $node, "start" => $this->getFrame(), "content" => []];
|
||||
//TODO $this->audio = (object)["node" => $node, "start" => $this->getFrame(), "content" => []];
|
||||
return $node["tagType"];
|
||||
case "SoundStreamBlock":
|
||||
if ($this->audio !== null) {
|
||||
if ($this->audio->start === null) {
|
||||
$this->audio->start = $this->getFrame();
|
||||
if($this->audio !== null){
|
||||
if($this->audio->getStartFrame() === null){
|
||||
$this->audio->setStartFrame($this->getFrame());
|
||||
}
|
||||
//$audio .= substr($data, 2);
|
||||
$this->audio->content[] = [$this->getFrame(), $node["soundStreamData"]];
|
||||
$this->audio->addStreamBlock($node);
|
||||
}
|
||||
return $node["tagType"];
|
||||
}
|
||||
|
|
10
swf2ass.php
10
swf2ass.php
|
@ -127,10 +127,10 @@ if ($swf->header["signature"]) {
|
|||
while(($frame = $processor->nextFrameOutput()) !== null){
|
||||
$audio = $processor->getAudio();
|
||||
if($audio !== null and $frameOffset === 0){
|
||||
if($audio->start === null){
|
||||
if($audio->getStartFrame() === null){
|
||||
continue;
|
||||
}
|
||||
$frameOffset = $audio->start;
|
||||
$frameOffset = $audio->getStartFrame();
|
||||
}
|
||||
$lastFrame = $frame;
|
||||
|
||||
|
@ -222,6 +222,12 @@ if ($swf->header["signature"]) {
|
|||
foreach ($assRenderer->flush($lastFrame) as $line){
|
||||
fwrite($fp, $line . "\n");
|
||||
}
|
||||
|
||||
if($processor->getAudio() !== null and $processor->getAudio()->getFormat() === \swf2ass\AudioStream::FORMAT_MP3){
|
||||
$audioFp = fopen($argv[2] . ".mp3", "w+");
|
||||
fwrite($audioFp, $processor->getAudio()->getAudioData());
|
||||
fclose($audioFp);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($testVectors) > 0){
|
||||
|
|
Loading…
Reference in a new issue