1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "kc/config.h"
21 #ifdef HAVE_LIBAVFORMAT
22
23 #include "kc/system.h"
24
25 #include "ui/gtk/ffmpeg.h"
26
FfmpegVideoEncoder(void)27 FfmpegVideoEncoder::FfmpegVideoEncoder(void)
28 {
29 _context = NULL;
30 _stream = NULL;
31 _frame = NULL;
32 _buf = NULL;
33 }
34
~FfmpegVideoEncoder(void)35 FfmpegVideoEncoder::~FfmpegVideoEncoder(void)
36 {
37 close();
38 }
39
40 bool
init(const char * filename,int width,int height,int fps_den,double quality)41 FfmpegVideoEncoder::init(const char *filename, int width, int height, int fps_den, double quality)
42 {
43 if (filename == NULL)
44 return false;
45
46 _width = width;
47 _height = height;
48
49 av_register_all();
50
51 AVOutputFormat *fmt = av_guess_format("avi", NULL, "video/x-msvideo");
52 if (fmt == NULL)
53 return false;
54
55 _context = avformat_alloc_context();
56 if (_context == NULL)
57 return false;
58
59 _context->oformat = fmt;
60 snprintf(_context->filename, sizeof (_context->filename), "%s", filename);
61
62 _stream = avformat_new_stream(_context, NULL);
63 if (_stream == NULL)
64 {
65 close();
66 return false;
67 }
68 _stream->id = 0;
69 _stream->codec->codec_id = fmt->video_codec;
70 _stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
71 _stream->codec->codec_tag = MKTAG('D', 'X', '5', '0');
72
73 _stream->codec->bit_rate = 79000 + 1000 * pow(1.4, quality * 20.0);
74 _stream->codec->width = width;
75 _stream->codec->height = height;
76 _stream->codec->time_base.den = 50;
77 _stream->codec->time_base.num = fps_den;
78 _stream->codec->gop_size = 100 / fps_den;
79 _stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
80
81 // some formats want stream headers to be separate
82 if (_context->oformat->flags & AVFMT_GLOBALHEADER)
83 _stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
84
85 AVCodec *codec = avcodec_find_encoder(_stream->codec->codec_id);
86 if (avcodec_open2(_stream->codec, codec, NULL) < 0)
87 {
88 close();
89 return false;
90 }
91
92 _bufsize = 10 * width * height;
93 _buf = (byte_t *)av_malloc(_bufsize);
94 if (_buf == NULL)
95 {
96 close();
97 return false;
98 }
99
100 _frame = av_frame_alloc();
101 if (_frame == NULL)
102 {
103 close();
104 return false;
105 }
106
107 int size = avpicture_get_size(_stream->codec->pix_fmt, width, height);
108 byte_t *buf = (byte_t *) av_malloc(size);
109 if (buf == NULL)
110 {
111 close();
112 return false;
113 }
114
115 avpicture_fill((AVPicture *) _frame, buf, _stream->codec->pix_fmt, width, height);
116
117 if (avio_open(&_context->pb, filename, AVIO_FLAG_WRITE) < 0)
118 {
119 close();
120 return false;
121 }
122
123 //dump_format(_context, 0, filename, 1);
124 avformat_write_header(_context, NULL);
125 return true;
126 }
127
128 void
allocate_color_rgb(int idx,int r,int g,int b)129 FfmpegVideoEncoder::allocate_color_rgb(int idx, int r, int g, int b)
130 {
131 _col[idx].y = (0.257 * r) + (0.504 * g) + (0.098 * b) + 16;
132 _col[idx].u = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128;
133 _col[idx].v = (0.439 * r) - (0.368 * g) - (0.071 * b) + 128;
134 }
135
136 bool
encode(byte_t * image,byte_t * dirty)137 FfmpegVideoEncoder::encode(byte_t *image, byte_t *dirty)
138 {
139 for (int y = 0;y < _height;y++)
140 {
141 int z = y * _frame->linesize[0];
142 for (int x = 0;x < _width;x++)
143 {
144 _frame->data[0][z + x] = _col[image[z + x]].y;
145 }
146 }
147 for (int y = 0;y < _height / 2;y++)
148 {
149 int z = 2 * y * _width;
150 for (int x = 0;x < _width;x += 2)
151 {
152 int u = _col[image[z + x]].u + _col[image[z + x + 1]].u + _col[image[z + x + _width]].u + _col[image[z + x + _width + 1]].u;
153 int v = _col[image[z + x]].v + _col[image[z + x + 1]].v + _col[image[z + x + _width]].v + _col[image[z + x + _width + 1]].v;
154 _frame->data[1][y * _frame->linesize[1] + x / 2] = u / 4;
155 _frame->data[2][y * _frame->linesize[2] + x / 2] = v / 4;
156 }
157 }
158
159 AVPacket pkt;
160 av_init_packet(&pkt);
161 pkt.data = _buf;
162 pkt.size = _bufsize;
163
164 int got_packet = 0;
165 int ret = avcodec_encode_video2(_stream->codec, &pkt, _frame, &got_packet);
166 if (ret < 0 || !got_packet || pkt.size <= 0)
167 return true;
168
169 if (_stream->codec->coded_frame->pts != AV_NOPTS_VALUE)
170 pkt.pts = av_rescale_q(_stream->codec->coded_frame->pts, _stream->codec->time_base, _stream->time_base);
171 if (_stream->codec->coded_frame->key_frame)
172 pkt.flags |= AV_PKT_FLAG_KEY;
173 pkt.stream_index = _stream->index;
174
175 return av_interleaved_write_frame(_context, &pkt) == 0;
176 }
177
178 void
close(void)179 FfmpegVideoEncoder::close(void)
180 {
181 if (_context == NULL)
182 return;
183
184 av_write_trailer(_context);
185
186 avcodec_close(_stream->codec);
187 av_free(_frame->data[0]);
188 av_free(_frame);
189 av_free(_buf);
190
191 for (unsigned int i = 0;i < _context->nb_streams;i++)
192 {
193 av_freep(&_context->streams[i]->codec);
194 av_freep(&_context->streams[i]);
195 }
196 avio_close(_context->pb);
197
198 av_free(_context);
199
200 _context = NULL;
201 _stream = NULL;
202 _frame = NULL;
203 _buf = NULL;
204 }
205
206 #endif /* HAVE_LIBAVFORMAT */
207