|
1 |
|
2 #include <stdlib.h> |
|
3 #include <stdio.h> |
|
4 #include <string.h> |
|
5 #include <stdarg.h> |
|
6 #include "libavformat/avformat.h" |
|
7 |
|
8 static AVFormatContext* g_pContainer; |
|
9 static AVOutputFormat* g_pFormat; |
|
10 static AVStream* g_pAStream; |
|
11 static AVStream* g_pVStream; |
|
12 static AVFrame* g_pAFrame; |
|
13 static AVFrame* g_pVFrame; |
|
14 static AVCodec* g_pACodec; |
|
15 static AVCodec* g_pVCodec; |
|
16 static AVCodecContext* g_pAudio; |
|
17 static AVCodecContext* g_pVideo; |
|
18 |
|
19 static int g_Width, g_Height, g_Framerate; |
|
20 static int g_Frequency, g_Channels; |
|
21 |
|
22 static FILE* g_pSoundFile; |
|
23 static int16_t* g_pSamples; |
|
24 static int g_NumSamples; |
|
25 |
|
26 /* |
|
27 Initially I wrote code for latest ffmpeg, but on Linux (Ubuntu) |
|
28 only older version is available from repository. That's why you see here |
|
29 all of this #if LIBAVCODEC_VERSION_MAJOR < 54. |
|
30 Actually, it may be possible to remove code for newer version |
|
31 and use only code for older version. |
|
32 */ |
|
33 |
|
34 #if LIBAVCODEC_VERSION_MAJOR < 54 |
|
35 #define OUTBUFFER_SIZE 200000 |
|
36 static uint8_t g_OutBuffer[OUTBUFFER_SIZE]; |
|
37 #endif |
|
38 |
|
39 // pointer to function from hwengine (uUtils.pas) |
|
40 static void (*AddFileLogRaw)(const char* pString); |
|
41 |
|
42 static void FatalError(const char* pFmt, ...) |
|
43 { |
|
44 const char Buffer[1024]; |
|
45 va_list VaArgs; |
|
46 |
|
47 va_start(VaArgs, pFmt); |
|
48 vsnprintf(Buffer, 1024, pFmt, VaArgs); |
|
49 va_end(VaArgs); |
|
50 |
|
51 AddFileLogRaw("Error in av-wrapper: "); |
|
52 AddFileLogRaw(Buffer); |
|
53 AddFileLogRaw("\n"); |
|
54 exit(1); |
|
55 } |
|
56 |
|
57 // Function to be called from libav for logging. |
|
58 // Note: libav can call LogCallback from different threads |
|
59 // (there is mutex in AddFileLogRaw). |
|
60 static void LogCallback(void* p, int Level, const char* pFmt, va_list VaArgs) |
|
61 { |
|
62 const char Buffer[1024]; |
|
63 |
|
64 vsnprintf(Buffer, 1024, pFmt, VaArgs); |
|
65 AddFileLogRaw(Buffer); |
|
66 } |
|
67 |
|
68 static void Log(const char* pFmt, ...) |
|
69 { |
|
70 const char Buffer[1024]; |
|
71 va_list VaArgs; |
|
72 |
|
73 va_start(VaArgs, pFmt); |
|
74 vsnprintf(Buffer, 1024, pFmt, VaArgs); |
|
75 va_end(VaArgs); |
|
76 |
|
77 AddFileLogRaw(Buffer); |
|
78 } |
|
79 |
|
80 static void AddAudioStream(enum CodecID codec_id) |
|
81 { |
|
82 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
83 g_pAStream = avformat_new_stream(g_pContainer, g_pACodec); |
|
84 #else |
|
85 g_pAStream = av_new_stream(g_pContainer, 1); |
|
86 #endif |
|
87 if(!g_pAStream) |
|
88 FatalError("Could not allocate audio stream"); |
|
89 g_pAStream->id = 1; |
|
90 |
|
91 g_pAudio = g_pAStream->codec; |
|
92 avcodec_get_context_defaults3(g_pAudio, g_pACodec); |
|
93 g_pAudio->codec_id = codec_id; |
|
94 |
|
95 // put parameters |
|
96 g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16; |
|
97 // pContext->bit_rate = 128000; |
|
98 g_pAudio->sample_rate = g_Frequency; |
|
99 g_pAudio->channels = g_Channels; |
|
100 |
|
101 // some formats want stream headers to be separate |
|
102 if (g_pFormat->flags & AVFMT_GLOBALHEADER) |
|
103 g_pAudio->flags |= CODEC_FLAG_GLOBAL_HEADER; |
|
104 |
|
105 // open it |
|
106 if (avcodec_open2(g_pAudio, g_pACodec, NULL) < 0) |
|
107 FatalError("Could not open audio codec %s", g_pACodec->long_name); |
|
108 |
|
109 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
110 if (g_pACodec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) |
|
111 #else |
|
112 if (g_pAudio->frame_size == 0) |
|
113 #endif |
|
114 g_NumSamples = 4096; |
|
115 else |
|
116 g_NumSamples = g_pAudio->frame_size; |
|
117 g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t)); |
|
118 g_pAFrame = avcodec_alloc_frame(); |
|
119 if (!g_pAFrame) |
|
120 FatalError("Could not allocate frame"); |
|
121 } |
|
122 |
|
123 // returns non-zero if there is more sound |
|
124 static int WriteAudioFrame() |
|
125 { |
|
126 AVPacket Packet = { 0 }; |
|
127 av_init_packet(&Packet); |
|
128 |
|
129 int NumSamples = fread(g_pSamples, 2*g_Channels, g_NumSamples, g_pSoundFile); |
|
130 |
|
131 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
132 AVFrame* pFrame = NULL; |
|
133 if (NumSamples > 0) |
|
134 { |
|
135 g_pAFrame->nb_samples = NumSamples; |
|
136 avcodec_fill_audio_frame(g_pAFrame, g_Channels, AV_SAMPLE_FMT_S16, |
|
137 (uint8_t*)g_pSamples, NumSamples*2*g_Channels, 1); |
|
138 pFrame = g_pAFrame; |
|
139 } |
|
140 // when NumSamples == 0 we still need to call encode_audio2 to flush |
|
141 int got_packet; |
|
142 if (avcodec_encode_audio2(g_pAudio, &Packet, pFrame, &got_packet) != 0) |
|
143 FatalError("avcodec_encode_audio2 failed"); |
|
144 if (!got_packet) |
|
145 return 0; |
|
146 #else |
|
147 if (NumSamples == 0) |
|
148 return 0; |
|
149 int BufferSize = OUTBUFFER_SIZE; |
|
150 if (g_pAudio->frame_size == 0) |
|
151 BufferSize = NumSamples*g_Channels*2; |
|
152 Packet.size = avcodec_encode_audio(g_pAudio, g_OutBuffer, BufferSize, g_pSamples); |
|
153 if (Packet.size == 0) |
|
154 return 1; |
|
155 if (g_pAudio->coded_frame && g_pAudio->coded_frame->pts != AV_NOPTS_VALUE) |
|
156 Packet.pts = av_rescale_q(g_pAudio->coded_frame->pts, g_pAudio->time_base, g_pAStream->time_base); |
|
157 Packet.flags |= AV_PKT_FLAG_KEY; |
|
158 Packet.data = g_OutBuffer; |
|
159 #endif |
|
160 |
|
161 // Write the compressed frame to the media file. |
|
162 Packet.stream_index = g_pAStream->index; |
|
163 if (av_interleaved_write_frame(g_pContainer, &Packet) != 0) |
|
164 FatalError("Error while writing audio frame"); |
|
165 return 1; |
|
166 } |
|
167 |
|
168 // add a video output stream |
|
169 static void AddVideoStream(enum CodecID codec_id) |
|
170 { |
|
171 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
172 g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec); |
|
173 #else |
|
174 g_pVStream = av_new_stream(g_pContainer, 0); |
|
175 #endif |
|
176 if (!g_pVStream) |
|
177 FatalError("Could not allocate video stream"); |
|
178 |
|
179 g_pVideo = g_pVStream->codec; |
|
180 avcodec_get_context_defaults3( g_pVideo, g_pVCodec ); |
|
181 g_pVideo->codec_id = codec_id; |
|
182 |
|
183 // put parameters |
|
184 // resolution must be a multiple of two |
|
185 g_pVideo->width = g_Width; |
|
186 g_pVideo->height = g_Height; |
|
187 /* time base: this is the fundamental unit of time (in seconds) in terms |
|
188 of which frame timestamps are represented. for fixed-fps content, |
|
189 timebase should be 1/framerate and timestamp increments should be |
|
190 identically 1. */ |
|
191 g_pVideo->time_base.den = g_Framerate; |
|
192 g_pVideo->time_base.num = 1; |
|
193 //g_pVideo->gop_size = 12; /* emit one intra frame every twelve frames at most */ |
|
194 g_pVideo->pix_fmt = PIX_FMT_YUV420P; |
|
195 |
|
196 // some formats want stream headers to be separate |
|
197 if (g_pFormat->flags & AVFMT_GLOBALHEADER) |
|
198 g_pVideo->flags |= CODEC_FLAG_GLOBAL_HEADER; |
|
199 |
|
200 AVDictionary* pDict = NULL; |
|
201 if (codec_id == CODEC_ID_H264) |
|
202 { |
|
203 // av_dict_set(&pDict, "tune", "animation", 0); |
|
204 // av_dict_set(&pDict, "preset", "veryslow", 0); |
|
205 av_dict_set(&pDict, "crf", "20", 0); |
|
206 } |
|
207 else |
|
208 { |
|
209 g_pVideo->flags |= CODEC_FLAG_QSCALE; |
|
210 // g_pVideo->bit_rate = g_Width*g_Height*g_Framerate/4; |
|
211 g_pVideo->global_quality = 15*FF_QP2LAMBDA; |
|
212 } |
|
213 |
|
214 // open the codec |
|
215 if (avcodec_open2(g_pVideo, g_pVCodec, &pDict) < 0) |
|
216 FatalError("Could not open video codec %s", g_pVCodec->long_name); |
|
217 |
|
218 g_pVFrame = avcodec_alloc_frame(); |
|
219 if (!g_pVFrame) |
|
220 FatalError("Could not allocate frame"); |
|
221 |
|
222 g_pVFrame->linesize[0] = g_Width; |
|
223 g_pVFrame->linesize[1] = g_Width/2; |
|
224 g_pVFrame->linesize[2] = g_Width/2; |
|
225 g_pVFrame->linesize[3] = 0; |
|
226 } |
|
227 |
|
228 static int WriteFrame( AVFrame* pFrame ) |
|
229 { |
|
230 double AudioTime, VideoTime; |
|
231 |
|
232 // write interleaved audio frame |
|
233 if (g_pAStream) |
|
234 { |
|
235 VideoTime = (double)g_pVStream->pts.val*g_pVStream->time_base.num/g_pVStream->time_base.den; |
|
236 do |
|
237 AudioTime = (double)g_pAStream->pts.val*g_pAStream->time_base.num/g_pAStream->time_base.den; |
|
238 while (AudioTime < VideoTime && WriteAudioFrame()); |
|
239 } |
|
240 |
|
241 AVPacket Packet; |
|
242 av_init_packet(&Packet); |
|
243 Packet.data = NULL; |
|
244 Packet.size = 0; |
|
245 |
|
246 g_pVFrame->pts++; |
|
247 if (g_pFormat->flags & AVFMT_RAWPICTURE) |
|
248 { |
|
249 /* raw video case. The API will change slightly in the near |
|
250 future for that. */ |
|
251 Packet.flags |= AV_PKT_FLAG_KEY; |
|
252 Packet.stream_index = g_pVStream->index; |
|
253 Packet.data = (uint8_t*)pFrame; |
|
254 Packet.size = sizeof(AVPicture); |
|
255 |
|
256 if (av_interleaved_write_frame(g_pContainer, &Packet) != 0) |
|
257 FatalError("Error while writing video frame"); |
|
258 return 0; |
|
259 } |
|
260 else |
|
261 { |
|
262 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
263 int got_packet; |
|
264 if (avcodec_encode_video2(g_pVideo, &Packet, pFrame, &got_packet) < 0) |
|
265 FatalError("avcodec_encode_video2 failed"); |
|
266 if (!got_packet) |
|
267 return 0; |
|
268 |
|
269 if (Packet.pts != AV_NOPTS_VALUE) |
|
270 Packet.pts = av_rescale_q(Packet.pts, g_pVideo->time_base, g_pVStream->time_base); |
|
271 if (Packet.dts != AV_NOPTS_VALUE) |
|
272 Packet.dts = av_rescale_q(Packet.dts, g_pVideo->time_base, g_pVStream->time_base); |
|
273 #else |
|
274 Packet.size = avcodec_encode_video(g_pVideo, g_OutBuffer, OUTBUFFER_SIZE, pFrame); |
|
275 if (Packet.size < 0) |
|
276 FatalError("avcodec_encode_video failed"); |
|
277 if (Packet.size == 0) |
|
278 return 0; |
|
279 |
|
280 if( g_pVideo->coded_frame->pts != AV_NOPTS_VALUE) |
|
281 Packet.pts = av_rescale_q(g_pVideo->coded_frame->pts, g_pVideo->time_base, g_pVStream->time_base); |
|
282 if( g_pVideo->coded_frame->key_frame ) |
|
283 Packet.flags |= AV_PKT_FLAG_KEY; |
|
284 Packet.data = g_OutBuffer; |
|
285 #endif |
|
286 // write the compressed frame in the media file |
|
287 Packet.stream_index = g_pVStream->index; |
|
288 if (av_interleaved_write_frame(g_pContainer, &Packet) != 0) |
|
289 FatalError("Error while writing video frame"); |
|
290 |
|
291 return 1; |
|
292 } |
|
293 } |
|
294 |
|
295 void AVWrapper_WriteFrame(uint8_t* pY, uint8_t* pCb, uint8_t* pCr) |
|
296 { |
|
297 g_pVFrame->data[0] = pY; |
|
298 g_pVFrame->data[1] = pCb; |
|
299 g_pVFrame->data[2] = pCr; |
|
300 WriteFrame(g_pVFrame); |
|
301 } |
|
302 |
|
303 void AVWrapper_GetList() |
|
304 { |
|
305 // initialize libav and register all codecs and formats |
|
306 av_register_all(); |
|
307 |
|
308 #if 0 |
|
309 AVOutputFormat* pFormat = NULL; |
|
310 while (pFormat = av_oformat_next(pFormat)) |
|
311 { |
|
312 Log("%s; %s; %s;\n", pFormat->name, pFormat->long_name, pFormat->mime_type); |
|
313 |
|
314 AVCodec* pCodec = NULL; |
|
315 while (pCodec = av_codec_next(pCodec)) |
|
316 { |
|
317 if (!av_codec_is_encoder(pCodec)) |
|
318 continue; |
|
319 if (avformat_query_codec(pFormat, pCodec->id, FF_COMPLIANCE_NORMAL) != 1) |
|
320 continue; |
|
321 if (pCodec->type = AVMEDIA_TYPE_VIDEO) |
|
322 { |
|
323 if (pCodec->supported_framerate != NULL) |
|
324 continue; |
|
325 Log(" Video: %s; %s;\n", pCodec->name, pCodec->long_name); |
|
326 } |
|
327 if (pCodec->type = AVMEDIA_TYPE_AUDIO) |
|
328 { |
|
329 /* if (pCodec->supported_samplerates == NULL) |
|
330 continue; |
|
331 int i; |
|
332 for(i = 0; i <) |
|
333 supported_samplerates*/ |
|
334 Log(" Audio: %s; %s;\n", pCodec->name, pCodec->long_name); |
|
335 } |
|
336 } |
|
337 /* struct AVCodecTag** pTags = pCur->codec_tag; |
|
338 int i; |
|
339 for (i = 0; ; i++) |
|
340 { |
|
341 enum CodecID id = av_codec_get_id(pTags, i); |
|
342 if (id == CODEC_ID_NONE) |
|
343 break; |
|
344 AVCodec* pCodec = avcodec_find_encoder(id); |
|
345 Log(" %i: %s; %s;\n", id, pCodec->name, pCodec->long_name); |
|
346 }*/ |
|
347 } |
|
348 #endif |
|
349 } |
|
350 |
|
351 void AVWrapper_Init(void (*pAddFileLogRaw)(const char*), const char* pFilename, const char* pSoundFile, int Width, int Height, int Framerate, int Frequency, int Channels) |
|
352 { |
|
353 AddFileLogRaw = pAddFileLogRaw; |
|
354 av_log_set_callback( &LogCallback ); |
|
355 |
|
356 g_Width = Width; |
|
357 g_Height = Height; |
|
358 g_Framerate = Framerate; |
|
359 g_Frequency = Frequency; |
|
360 g_Channels = Channels; |
|
361 |
|
362 // initialize libav and register all codecs and formats |
|
363 av_register_all(); |
|
364 |
|
365 AVWrapper_GetList(); |
|
366 |
|
367 // allocate the output media context |
|
368 #if LIBAVCODEC_VERSION_MAJOR >= 54 |
|
369 avformat_alloc_output_context2(&g_pContainer, NULL, "mp4", pFilename); |
|
370 #else |
|
371 g_pFormat = av_guess_format(NULL, pFilename, NULL); |
|
372 if (!g_pFormat) |
|
373 FatalError("guess_format"); |
|
374 |
|
375 // allocate the output media context |
|
376 g_pContainer = avformat_alloc_context(); |
|
377 if (g_pContainer) |
|
378 { |
|
379 g_pContainer->oformat = g_pFormat; |
|
380 snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s", pFilename); |
|
381 } |
|
382 #endif |
|
383 if (!g_pContainer) |
|
384 FatalError("Could not allocate output context"); |
|
385 |
|
386 g_pFormat = g_pContainer->oformat; |
|
387 |
|
388 enum CodecID VideoCodecID = g_pFormat->video_codec;//CODEC_ID_H264; |
|
389 enum CodecID AudioCodecID = g_pFormat->audio_codec; |
|
390 |
|
391 g_pVStream = NULL; |
|
392 g_pAStream = NULL; |
|
393 if (VideoCodecID != CODEC_ID_NONE) |
|
394 { |
|
395 g_pVCodec = avcodec_find_encoder(VideoCodecID); |
|
396 if (!g_pVCodec) |
|
397 FatalError("Video codec not found"); |
|
398 AddVideoStream(VideoCodecID); |
|
399 } |
|
400 |
|
401 if (AudioCodecID != CODEC_ID_NONE) |
|
402 { |
|
403 g_pACodec = avcodec_find_encoder(AudioCodecID); |
|
404 if (!g_pACodec) |
|
405 FatalError("Audio codec not found"); |
|
406 AddAudioStream(AudioCodecID); |
|
407 } |
|
408 |
|
409 if (g_pAStream) |
|
410 { |
|
411 g_pSoundFile = fopen(pSoundFile, "rb"); |
|
412 if (!g_pSoundFile) |
|
413 FatalError("Could not open %s", pSoundFile); |
|
414 } |
|
415 |
|
416 // write format info to log |
|
417 av_dump_format(g_pContainer, 0, pFilename, 1); |
|
418 |
|
419 // open the output file, if needed |
|
420 if (!(g_pFormat->flags & AVFMT_NOFILE)) |
|
421 { |
|
422 if (avio_open(&g_pContainer->pb, pFilename, AVIO_FLAG_WRITE) < 0) |
|
423 FatalError("Could not open output file (%s)", pFilename); |
|
424 } |
|
425 |
|
426 // write the stream header, if any |
|
427 avformat_write_header(g_pContainer, NULL); |
|
428 g_pVFrame->pts = -1; |
|
429 } |
|
430 |
|
431 void AVWrapper_Close() |
|
432 { |
|
433 // output buffered frames |
|
434 if (g_pVCodec->capabilities & CODEC_CAP_DELAY) |
|
435 while( WriteFrame(NULL) ); |
|
436 // output any remaining audio |
|
437 while( WriteAudioFrame() ); |
|
438 |
|
439 // write the trailer, if any. |
|
440 av_write_trailer(g_pContainer); |
|
441 |
|
442 // close each codec |
|
443 if( g_pVStream ) |
|
444 { |
|
445 avcodec_close(g_pVStream->codec); |
|
446 av_free(g_pVFrame); |
|
447 } |
|
448 if( g_pAStream ) |
|
449 { |
|
450 avcodec_close(g_pAStream->codec); |
|
451 av_free(g_pAFrame); |
|
452 av_free(g_pSamples); |
|
453 fclose(g_pSoundFile); |
|
454 } |
|
455 |
|
456 // free the streams |
|
457 int i; |
|
458 for (i = 0; i < g_pContainer->nb_streams; i++) |
|
459 { |
|
460 av_freep(&g_pContainer->streams[i]->codec); |
|
461 av_freep(&g_pContainer->streams[i]); |
|
462 } |
|
463 |
|
464 // close the output file |
|
465 if (!(g_pFormat->flags & AVFMT_NOFILE)) |
|
466 avio_close(g_pContainer->pb); |
|
467 |
|
468 // free the stream |
|
469 av_free(g_pContainer); |
|
470 } |