forked from sunlubo/SwiftFFmpeg
-
Notifications
You must be signed in to change notification settings - Fork 1
/
encode_video.swift
120 lines (98 loc) · 2.96 KB
/
encode_video.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//
// encode_video.swift
// Demo
//
// Created by sunlubo on 2018/7/3.
//
import SwiftFFmpeg
#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif
private func encode(
codecCtx: AVCodecContext,
frame: AVFrame?,
pkt: AVPacket,
file: UnsafeMutablePointer<FILE>
) throws {
if let frame = frame {
print(String(format: "Send frame %3d", frame.pts))
}
// send the frame to the encoder
try codecCtx.sendFrame(frame)
while true {
do {
try codecCtx.receivePacket(pkt)
} catch let err as AVError where err == .tryAgain || err == .eof {
return
}
print(String(format: "Write packet %3d (size=%5d)", pkt.pts, pkt.size))
fwrite(pkt.data, 1, pkt.size, file)
pkt.unref()
}
}
func encode_video() throws {
if CommandLine.argc < 3 {
print("Usage: \(CommandLine.arguments[0]) \(CommandLine.arguments[1]) output_file")
return
}
let output = CommandLine.arguments[2]
// find the mpeg1video encoder
let codec = AVCodec.findEncoderByName("mpeg1video")!
let codecCtx = AVCodecContext(codec: codec)
// put sample parameters
codecCtx.bitRate = 400000
// resolution must be a multiple of two
codecCtx.width = 352
codecCtx.height = 288
// frames per second
codecCtx.timebase = AVRational(num: 1, den: 25)
codecCtx.framerate = AVRational(num: 25, den: 1)
// emit one intra frame every ten frames
// check frame pict_type before passing frame
// to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
// then gop_size is ignored and the output of encoder
// will always be I frame irrespective to gop_size
codecCtx.gopSize = 10
codecCtx.maxBFrames = 1
codecCtx.pixelFormat = .YUV420P
try codecCtx.openCodec()
guard let file = fopen(output, "wb") else {
fatalError("Could not open \(output)")
}
defer { fclose(file) }
let pkt = AVPacket()
let frame = AVFrame()
frame.pixelFormat = codecCtx.pixelFormat
frame.width = codecCtx.width
frame.height = codecCtx.height
try frame.allocBuffer(align: 32)
// encode 1 second of video
for i in 0..<25 {
// make sure the frame data is writable
try frame.makeWritable()
// prepare a dummy image
// Y
for y in 0..<codecCtx.height {
for x in 0..<codecCtx.width {
frame.data[0]![y * Int(frame.linesize[0]) + x] = UInt8(truncatingIfNeeded: x + y + i * 3)
}
}
// Cb and Cr
for y in 0..<codecCtx.height / 2 {
for x in 0..<codecCtx.width / 2 {
frame.data[1]![y * Int(frame.linesize[1]) + x] = UInt8(truncatingIfNeeded: 128 + y + i * 2)
frame.data[2]![y * Int(frame.linesize[2]) + x] = UInt8(truncatingIfNeeded: 64 + x + i * 5)
}
}
frame.pts = Int64(i)
// encode the image
try encode(codecCtx: codecCtx, frame: frame, pkt: pkt, file: file)
}
// flush the encoder
try encode(codecCtx: codecCtx, frame: nil, pkt: pkt, file: file)
// add sequence end code to have a real MPEG file
let endcode = [0, 0, 1, 0xb7] as [UInt8]
fwrite(endcode, 1, endcode.count, file)
}