Refactoring

This commit is contained in:
shogo4405 2016-12-14 04:54:24 +09:00
parent fd1df9c02e
commit b25f5235ae
9 changed files with 84 additions and 77 deletions

View File

@ -1,4 +1,5 @@
import Foundation
import AVFoundation
class MP4Box {
static func create(_ data:Data) throws -> MP4Box {
@ -515,16 +516,15 @@ final class MP4EditListBox: MP4Box {
// MARK: -
final class MP4Reader: MP4ContainerBox {
var url:URL? = nil {
didSet {
if (url == nil) {
return
}
do {
fileHandle = try FileHandle(forReadingFrom: url!)
} catch let error as NSError {
print(error)
}
private(set) var url:URL
init(url:URL) {
do {
self.url = url
super.init()
fileHandle = try FileHandle(forReadingFrom: url)
} catch let error as NSError {
logger.error("\(error)")
}
}
@ -581,7 +581,7 @@ final class MP4TrakReader {
weak var delegate:MP4SamplerDelegate?
fileprivate var id:Int = 0
fileprivate var reader:FileHandle?
fileprivate var handle:FileHandle?
private lazy var timerDriver:TimerDriver = {
var timerDriver:TimerDriver = TimerDriver()
timerDriver.delegate = self
@ -664,16 +664,24 @@ final class MP4TrakReader {
totalTimeToSample = timeToSample[cursor]
}
func execute(url:URL) {
func execute(_ reader:MP4Reader) {
do {
reader = try FileHandle(forReadingFrom: url)
handle = try FileHandle(forReadingFrom: reader.url)
if let avcC:MP4Box = trak.getBoxes(byName: "avcC").first {
delegate?.didSet(config: reader.readData(ofBox: avcC), withID: id, type: AVMediaTypeVideo)
}
if let esds:MP4ElementaryStreamDescriptorBox = trak.getBoxes(byName: "esds").first as? MP4ElementaryStreamDescriptorBox {
delegate?.didSet(config: Data(esds.audioDecorderSpecificConfig), withID: id, type: AVMediaTypeAudio)
}
timerDriver.interval = MachUtil.nanosToAbs(UInt64(currentTimeToSample * 1000 * 1000))
while (currentDuration <= bufferTime) {
tick(timerDriver)
}
timerDriver.startRunning()
} catch {
logger.warning("file open error :\(url)")
logger.warning("file open error :\(reader.url)")
}
}
@ -692,16 +700,16 @@ final class MP4TrakReader {
extension MP4TrakReader: TimerDriverDelegate {
// MARK: TimerDriverDelegate
func tick(_ driver:TimerDriver) {
guard let reader:FileHandle = reader else {
guard let handle:FileHandle = handle else {
driver.stopRunning()
return
}
driver.interval = MachUtil.nanosToAbs(UInt64(currentTimeToSample * 1000 * 1000))
reader.seek(toFileOffset: currentOffset)
handle.seek(toFileOffset: currentOffset)
autoreleasepool {
delegate?.output(
data: reader.readData(ofLength: currentSampleSize),
withType: id,
data: handle.readData(ofLength: currentSampleSize),
withID: id,
currentTime: currentTimeToSample,
keyframe: currentIsKeyframe
)

View File

@ -1,10 +1,9 @@
import Foundation
// MARK: -
protocol MP4SamplerDelegate: class {
func didSet(avcC:Data, withType:Int)
func didSet(audioDecorderSpecificConfig:Data, withType:Int)
func output(data:Data, withType:Int, currentTime:Double, keyframe:Bool)
func didOpen(_ reader:MP4Reader)
func didSet(config:Data, withID:Int, type:String)
func output(data:Data, withID:Int, currentTime:Double, keyframe:Bool)
}
// MARK: -
@ -13,26 +12,22 @@ class MP4Sampler {
weak var delegate:MP4SamplerDelegate?
fileprivate(set) var running:Bool = false
fileprivate var files:[URL] = []
fileprivate var handlers:[URL:Handler?] = [:]
fileprivate let lockQueue:DispatchQueue = DispatchQueue(label: "com.github.shogo4405.lf.MP4Sampler.lock")
fileprivate let loopQueue:DispatchQueue = DispatchQueue(label: "com.github.shgoo4405.lf.MP4Sampler.loop")
fileprivate let operations:OperationQueue = OperationQueue()
private var reader:MP4Reader = MP4Reader()
private var trakReaders:[MP4TrakReader] = []
fileprivate(set) var running:Bool = false
func appendFile(_ file:URL, completionHandler: Handler? = nil) {
lockQueue.async {
self.files.append(file)
self.handlers[file] = completionHandler
self.files.append(file)
}
}
fileprivate func execute(url:URL) {
reader.url = url
let reader:MP4Reader = MP4Reader(url: url)
do {
let _:UInt32 = try reader.load()
@ -41,25 +36,13 @@ class MP4Sampler {
return
}
trakReaders.removeAll()
delegate?.didOpen(reader)
let traks:[MP4Box] = reader.getBoxes(byName: "trak")
for i in 0..<traks.count {
trakReaders.append(MP4TrakReader(id:i, trak:traks[i]))
}
for i in 0..<trakReaders.count {
if let avcC:MP4Box = trakReaders[i].trak.getBoxes(byName: "avcC").first {
delegate?.didSet(avcC: reader.readData(ofBox: avcC), withType: i)
}
if let esds:MP4ElementaryStreamDescriptorBox = trakReaders[i].trak.getBoxes(byName: "esds").first as? MP4ElementaryStreamDescriptorBox {
delegate?.didSet(audioDecorderSpecificConfig: Data(esds.audioDecorderSpecificConfig), withType: i)
}
}
for reader in trakReaders {
reader.delegate = delegate
let trakReader:MP4TrakReader = MP4TrakReader(id:i, trak:traks[i])
trakReader.delegate = delegate
operations.addOperation {
reader.execute(url: url)
trakReader.execute(reader)
}
}
operations.waitUntilAllOperationsAreFinished()

View File

@ -2,6 +2,7 @@ import Foundation
import AVFoundation
protocol RTMPMuxerDelegate: class {
func metadata(_ metadata:ASObject)
func sampleOutput(audio buffer:Data, withTimestamp:Double, muxer:RTMPMuxer)
func sampleOutput(video buffer:Data, withTimestamp:Double, muxer:RTMPMuxer)
}
@ -11,14 +12,12 @@ final class RTMPMuxer {
static let aac:UInt8 = FLVAudioCodec.aac.rawValue << 4 | FLVSoundRate.kHz44.rawValue << 2 | FLVSoundSize.snd16bit.rawValue << 1 | FLVSoundType.stereo.rawValue
weak var delegate:RTMPMuxerDelegate? = nil
fileprivate var avcC:Data?
fileprivate var audioDecorderSpecificConfig:Data?
fileprivate var configs:[Int:Data] = [:]
fileprivate var audioTimestamp:CMTime = kCMTimeZero
fileprivate var videoTimestamp:CMTime = kCMTimeZero
func dispose() {
avcC = nil
audioDecorderSpecificConfig = nil
configs.removeAll()
audioTimestamp = kCMTimeZero
videoTimestamp = kCMTimeZero
}
@ -100,32 +99,44 @@ extension RTMPMuxer: VideoEncoderDelegate {
}
extension RTMPMuxer: MP4SamplerDelegate {
// MP4SampleDelegate
func didSet(avcC: Data, withType:Int) {
if (avcC == self.avcC) {
return
// MARK: MP4SampleDelegate
func didOpen(_ reader: MP4Reader) {
var metadata:ASObject = ASObject()
if let avc1:MP4VisualSampleEntryBox = reader.getBoxes(byName: "avc1").first as? MP4VisualSampleEntryBox {
metadata["width"] = avc1.width
metadata["height"] = avc1.height
metadata["videocodecid"] = FLVVideoCodec.avc.rawValue
}
var buffer:Data = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(avcC)
delegate?.sampleOutput(video: buffer, withTimestamp: 0, muxer: self)
self.avcC = avcC
if let _:MP4AudioSampleEntryBox = reader.getBoxes(byName: "mp4a").first as? MP4AudioSampleEntryBox {
metadata["audiocodecid"] = FLVAudioCodec.aac.rawValue
}
delegate?.metadata(metadata)
}
func didSet(audioDecorderSpecificConfig:Data, withType:Int) {
if (withType == 2) {
func didSet(config:Data, withID:Int, type:String) {
guard configs[withID] != config else {
return
}
if (audioDecorderSpecificConfig == self.audioDecorderSpecificConfig) {
return
configs[withID] = config
switch type {
case AVMediaTypeVideo:
var buffer:Data = Data([FLVFrameType.key.rawValue << 4 | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.seq.rawValue, 0, 0, 0])
buffer.append(config)
delegate?.sampleOutput(video: buffer, withTimestamp: 0, muxer: self)
case AVMediaTypeAudio:
if (withID != 1) {
break
}
var buffer:Data = Data([RTMPMuxer.aac, FLVAACPacketType.seq.rawValue])
buffer.append(config)
delegate?.sampleOutput(audio: buffer, withTimestamp: 0, muxer: self)
default:
break
}
var buffer:Data = Data([RTMPMuxer.aac, FLVAACPacketType.seq.rawValue])
buffer.append(audioDecorderSpecificConfig)
delegate?.sampleOutput(audio: buffer, withTimestamp: 0, muxer: self)
self.audioDecorderSpecificConfig = audioDecorderSpecificConfig
}
func output(data:Data, withType:Int, currentTime:Double, keyframe:Bool) {
switch withType {
func output(data:Data, withID:Int, currentTime:Double, keyframe:Bool) {
switch withID {
case 0:
let compositionTime:Int32 = 0
var buffer:Data = Data([((keyframe ? FLVFrameType.key.rawValue : FLVFrameType.inter.rawValue) << 4) | FLVVideoCodec.avc.rawValue, FLVAVCPacketType.nal.rawValue])

View File

@ -606,6 +606,10 @@ extension RTMPStream: IEventDispatcher {
extension RTMPStream: RTMPMuxerDelegate {
// MARK: RTMPMuxerDelegate
func metadata(_ metadata:ASObject) {
send(handlerName: "@setDataFrame", arguments: "onMetaData", metadata)
}
func sampleOutput(audio buffer:Data, withTimestamp:Double, muxer:RTMPMuxer) {
guard readyState == .publishing else {
return

View File

@ -17,7 +17,7 @@ class TimerDriver {
weak var delegate:TimerDriverDelegate?
fileprivate var runloop:RunLoop?
fileprivate var lastFired:UInt64 = 0
fileprivate var nextFire:UInt64 = 0
fileprivate weak var timer:Timer? {
didSet {
if let oldValue:Timer = oldValue {
@ -30,12 +30,11 @@ class TimerDriver {
}
@objc func on(timer:Timer) {
let now:UInt64 = mach_absolute_time()
guard interval <= now - lastFired else {
guard nextFire <= mach_absolute_time() else {
return
}
lastFired = now
delegate?.tick(self)
nextFire += interval
}
}
@ -59,6 +58,8 @@ extension TimerDriver: Runnable {
timer = Timer(
timeInterval: 0.0001, target: self, selector: #selector(TimerDriver.on(timer:)), userInfo: nil, repeats: true
)
nextFire = mach_absolute_time() + interval
delegate?.tick(self)
runloop = .current
runloop?.run()
}

View File

@ -4,7 +4,7 @@ import XCTest
@testable import lf
final class MP4SamplerTests: XCTestCase {
func testMain() {
func main() {
let bundle:Bundle = Bundle(for: type(of: self))
let url:URL = URL(fileURLWithPath: bundle.path(forResource: "SampleVideo_360x240_5mb", ofType: "mp4")!)
let sampler:MP4Sampler = MP4Sampler()

View File

@ -1092,7 +1092,7 @@
attributes = {
LastSwiftMigration = 0700;
LastSwiftUpdateCheck = 0800;
LastUpgradeCheck = 0810;
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = "Shogo Endo";
TargetAttributes = {
2915EC511D85BDF100621092 = {
@ -1753,7 +1753,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = SUEQ2SZ2L5;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -1782,7 +1782,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = SUEQ2SZ2L5;
DYLIB_COMPATIBILITY_VERSION = 1;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0810"
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0810"
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"