xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp (revision da4dbfa47a47beb355289f3dd685797cee69ab77)
1 /*
2  * Copyright 2009-2010, Stephan Amßus <superstippi@gmx.de>
3  * Copyright 2018, Dario Casalinuovo
4  * All rights reserved. Distributed under the terms of the MIT license.
5  */
6 
7 
8 #include "AVCodecEncoder.h"
9 
10 #include <new>
11 
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include <Application.h>
16 #include <Roster.h>
17 
18 extern "C" {
19 	#include "rational.h"
20 }
21 
22 #include "EncoderTable.h"
23 #include "gfx_util.h"
24 
25 
26 #undef TRACE
27 //#define TRACE_AV_CODEC_ENCODER
28 #ifdef TRACE_AV_CODEC_ENCODER
29 #	define TRACE	printf
30 #	define TRACE_IO(a...)
31 #else
32 #	define TRACE(a...)
33 #	define TRACE_IO(a...)
34 #endif
35 
36 
37 static const size_t kDefaultChunkBufferSize = 2 * 1024 * 1024;
38 
39 
40 AVCodecEncoder::AVCodecEncoder(uint32 codecID, int bitRateScale)
41 	:
42 	Encoder(),
43 	fBitRateScale(bitRateScale),
44 	fCodecID((CodecID)codecID),
45 	fCodec(NULL),
46 	fCodecContext(NULL),
47 	fCodecInitStatus(CODEC_INIT_NEEDED),
48 	fFrame(av_frame_alloc()),
49 	fSwsContext(NULL),
50 	fFramesWritten(0)
51 {
52 	TRACE("AVCodecEncoder::AVCodecEncoder()\n");
53 	_Init();
54 }
55 
56 
57 void
58 AVCodecEncoder::_Init()
59 {
60 	fChunkBuffer = new(std::nothrow) uint8[kDefaultChunkBufferSize];
61 	if (fCodecID > 0) {
62 		fCodec = avcodec_find_encoder(fCodecID);
63 		TRACE("  found AVCodec for %u: %p\n", fCodecID, fCodec);
64 	}
65 
66 	fAudioFifo = av_fifo_alloc(0);
67 
68 	// Initial parameters, so we know if the user changed them
69 	fEncodeParameters.avg_field_size = 0;
70 	fEncodeParameters.max_field_size = 0;
71 	fEncodeParameters.quality = 1.0f;
72 }
73 
74 
75 AVCodecEncoder::~AVCodecEncoder()
76 {
77 	TRACE("AVCodecEncoder::~AVCodecEncoder()\n");
78 
79 	if (fSwsContext != NULL)
80 		sws_freeContext(fSwsContext);
81 
82 	av_fifo_free(fAudioFifo);
83 
84 	if (fFrame != NULL) {
85 		av_frame_free(&fFrame);
86 	}
87 
88 	if (fCodecContext != NULL) {
89 		avcodec_close(fCodecContext);
90 		avcodec_free_context(&fCodecContext);
91 	}
92 
93 	delete[] fChunkBuffer;
94 }
95 
96 
97 status_t
98 AVCodecEncoder::AcceptedFormat(const media_format* proposedInputFormat,
99 	media_format* _acceptedInputFormat)
100 {
101 	TRACE("AVCodecEncoder::AcceptedFormat(%p, %p)\n", proposedInputFormat,
102 		_acceptedInputFormat);
103 
104 	if (proposedInputFormat == NULL)
105 		return B_BAD_VALUE;
106 
107 	if (_acceptedInputFormat != NULL) {
108 		*_acceptedInputFormat = *proposedInputFormat;
109 	}
110 
111 	return B_OK;
112 }
113 
114 
115 status_t
116 AVCodecEncoder::SetUp(const media_format* inputFormat)
117 {
118 	TRACE("AVCodecEncoder::SetUp()\n");
119 
120 	if (inputFormat == NULL)
121 		return B_BAD_VALUE;
122 
123 	// Codec IDs for raw-formats may need to be figured out here.
124 	if (fCodec == NULL && fCodecID == AV_CODEC_ID_NONE) {
125 		fCodecID = raw_audio_codec_id_for(*inputFormat);
126 		if (fCodecID != AV_CODEC_ID_NONE)
127 			fCodec = avcodec_find_encoder(fCodecID);
128 	}
129 	if (fCodec == NULL) {
130 		TRACE("  encoder not found!\n");
131 		return B_NO_INIT;
132 	}
133 
134 	fInputFormat = *inputFormat;
135 	fFramesWritten = 0;
136 
137 	return _Setup();
138 }
139 
140 
141 status_t
142 AVCodecEncoder::GetEncodeParameters(encode_parameters* parameters) const
143 {
144 	TRACE("AVCodecEncoder::GetEncodeParameters(%p)\n", parameters);
145 
146 // TODO: Implement maintaining an automatically calculated bit_rate versus
147 // a user specified (via SetEncodeParameters()) bit_rate. At this point, the
148 // fCodecContext->bit_rate may not yet have been specified (_Setup() was never
149 // called yet). So it cannot work like the code below, but in any case, it's
150 // showing how to convert between the values (albeit untested).
151 //	int avgBytesPerSecond = fCodecContext->bit_rate / 8;
152 //	int maxBytesPerSecond = (fCodecContext->bit_rate
153 //		+ fCodecContext->bit_rate_tolerance) / 8;
154 //
155 //	if (fInputFormat.type == B_MEDIA_RAW_AUDIO) {
156 //		fEncodeParameters.avg_field_size = (int32)(avgBytesPerSecond
157 //			/ fInputFormat.u.raw_audio.frame_rate);
158 //		fEncodeParameters.max_field_size = (int32)(maxBytesPerSecond
159 //			/ fInputFormat.u.raw_audio.frame_rate);
160 //	} else if (fInputFormat.type == B_MEDIA_RAW_VIDEO) {
161 //		fEncodeParameters.avg_field_size = (int32)(avgBytesPerSecond
162 //			/ fInputFormat.u.raw_video.field_rate);
163 //		fEncodeParameters.max_field_size = (int32)(maxBytesPerSecond
164 //			/ fInputFormat.u.raw_video.field_rate);
165 //	}
166 
167 	parameters->quality = fEncodeParameters.quality;
168 
169 	return B_OK;
170 }
171 
172 
173 status_t
174 AVCodecEncoder::SetEncodeParameters(encode_parameters* parameters)
175 {
176 	TRACE("AVCodecEncoder::SetEncodeParameters(%p)\n", parameters);
177 
178 	if (fFramesWritten > 0)
179 		return B_NOT_SUPPORTED;
180 
181 	fEncodeParameters.quality = parameters->quality;
182 	TRACE("  quality: %.5f\n", parameters->quality);
183 	if (fEncodeParameters.quality == 0.0f) {
184 		TRACE("  using default quality (1.0)\n");
185 		fEncodeParameters.quality = 1.0f;
186 	}
187 
188 // TODO: Auto-bit_rate versus user supplied. See above.
189 //	int avgBytesPerSecond = 0;
190 //	int maxBytesPerSecond = 0;
191 //
192 //	if (fInputFormat.type == B_MEDIA_RAW_AUDIO) {
193 //		avgBytesPerSecond = (int)(parameters->avg_field_size
194 //			* fInputFormat.u.raw_audio.frame_rate);
195 //		maxBytesPerSecond = (int)(parameters->max_field_size
196 //			* fInputFormat.u.raw_audio.frame_rate);
197 //	} else if (fInputFormat.type == B_MEDIA_RAW_VIDEO) {
198 //		avgBytesPerSecond = (int)(parameters->avg_field_size
199 //			* fInputFormat.u.raw_video.field_rate);
200 //		maxBytesPerSecond = (int)(parameters->max_field_size
201 //			* fInputFormat.u.raw_video.field_rate);
202 //	}
203 //
204 //	if (maxBytesPerSecond < avgBytesPerSecond)
205 //		maxBytesPerSecond = avgBytesPerSecond;
206 //
207 //	// Reset these, so we can tell the difference between uninitialized
208 //	// and initialized...
209 //	if (avgBytesPerSecond > 0) {
210 //		fCodecContext->bit_rate = avgBytesPerSecond * 8;
211 //		fCodecContext->bit_rate_tolerance = (maxBytesPerSecond
212 //			- avgBytesPerSecond) * 8;
213 //		fBitRateControlledByUser = true;
214 //	}
215 
216 	return _Setup();
217 }
218 
219 
220 status_t
221 AVCodecEncoder::Encode(const void* buffer, int64 frameCount,
222 	media_encode_info* info)
223 {
224 	TRACE("AVCodecEncoder::Encode(%p, %" B_PRId64 ", %p)\n", buffer, frameCount, info);
225 
226 	if (!_OpenCodecIfNeeded())
227 		return B_NO_INIT;
228 
229 	if (fInputFormat.type == B_MEDIA_RAW_AUDIO)
230 		return _EncodeAudio(buffer, frameCount, info);
231 	else if (fInputFormat.type == B_MEDIA_RAW_VIDEO)
232 		return _EncodeVideo(buffer, frameCount, info);
233 	else
234 		return B_NO_INIT;
235 }
236 
237 
238 // #pragma mark -
239 
240 
241 status_t
242 AVCodecEncoder::_Setup()
243 {
244 	TRACE("AVCodecEncoder::_Setup\n");
245 
246 	int rawBitRate;
247 
248 	if (fCodecContext != NULL) {
249 		avcodec_close(fCodecContext);
250 		avcodec_free_context(&fCodecContext);
251 	}
252 
253 	fCodecContext = avcodec_alloc_context3(fCodec);
254 	if (fCodecContext == NULL)
255 		return B_NO_INIT;
256 
257 	if (fInputFormat.type == B_MEDIA_RAW_VIDEO) {
258 		TRACE("  B_MEDIA_RAW_VIDEO\n");
259 
260 		// Check input parameters
261 		AVPixelFormat pixFmt = colorspace_to_pixfmt(
262 			fInputFormat.u.raw_video.display.format);
263 		if (pixFmt == AV_PIX_FMT_NONE) {
264 			TRACE("Invalid input colorspace\n");
265 			return B_BAD_DATA;
266 		}
267 
268 		// frame rate
269 		fCodecContext->time_base = (AVRational){1, (int)fInputFormat.u.raw_video.field_rate};
270 		fCodecContext->framerate = (AVRational){(int)fInputFormat.u.raw_video.field_rate, 1};
271 
272 		// video size
273 		fCodecContext->width = fInputFormat.u.raw_video.display.line_width;
274 		fCodecContext->height = fInputFormat.u.raw_video.display.line_count;
275 		fCodecContext->gop_size = 12;
276 
277 		// TODO: Fix pixel format or setup conversion method...
278 		if (fCodec->pix_fmts != NULL) {
279 			for (int i = 0; fCodec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
280 				// Use the last supported pixel format, which we hope is the
281 				// one with the best quality.
282 				fCodecContext->pix_fmt = fCodec->pix_fmts[i];
283 			}
284 		}
285 
286 		// TODO: Setup rate control:
287 //		fCodecContext->rate_emu = 0;
288 //		fCodecContext->rc_eq = NULL;
289 //		fCodecContext->rc_max_rate = 0;
290 //		fCodecContext->rc_min_rate = 0;
291 		// TODO: Try to calculate a good bit rate...
292 		rawBitRate = (int)(fCodecContext->width * fCodecContext->height * 2
293 			* fInputFormat.u.raw_video.field_rate) * 8;
294 
295 		// Pixel aspect ratio
296 		fCodecContext->sample_aspect_ratio.num
297 			= fInputFormat.u.raw_video.pixel_width_aspect;
298 		fCodecContext->sample_aspect_ratio.den
299 			= fInputFormat.u.raw_video.pixel_height_aspect;
300 		if (fCodecContext->sample_aspect_ratio.num == 0
301 			|| fCodecContext->sample_aspect_ratio.den == 0) {
302 			av_reduce(&fCodecContext->sample_aspect_ratio.num,
303 				&fCodecContext->sample_aspect_ratio.den, fCodecContext->width,
304 				fCodecContext->height, 255);
305 		}
306 
307 		// TODO: This should already happen in AcceptFormat()
308 		if (fInputFormat.u.raw_video.display.bytes_per_row == 0) {
309 			fInputFormat.u.raw_video.display.bytes_per_row
310 				= fCodecContext->width * 4;
311 		}
312 
313 		fFrame->pts = 0;
314 
315 		fSwsContext = sws_getContext(fCodecContext->width,
316 			fCodecContext->height, pixFmt,
317 			fCodecContext->width, fCodecContext->height,
318 			fCodecContext->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
319 
320 	} else if (fInputFormat.type == B_MEDIA_RAW_AUDIO) {
321 		TRACE("  B_MEDIA_RAW_AUDIO\n");
322 		// frame rate
323 		fCodecContext->sample_rate = (int)fInputFormat.u.raw_audio.frame_rate;
324 		// channels
325 		fCodecContext->channels = fInputFormat.u.raw_audio.channel_count;
326 		// raw bitrate
327 		rawBitRate = fCodecContext->sample_rate * fCodecContext->channels
328 			* (fInputFormat.u.raw_audio.format
329 				& media_raw_audio_format::B_AUDIO_SIZE_MASK) * 8;
330 		// sample format
331 		switch (fInputFormat.u.raw_audio.format) {
332 			case media_raw_audio_format::B_AUDIO_FLOAT:
333 				fCodecContext->sample_fmt = AV_SAMPLE_FMT_FLT;
334 				break;
335 			case media_raw_audio_format::B_AUDIO_DOUBLE:
336 				fCodecContext->sample_fmt = AV_SAMPLE_FMT_DBL;
337 				break;
338 			case media_raw_audio_format::B_AUDIO_INT:
339 				fCodecContext->sample_fmt = AV_SAMPLE_FMT_S32;
340 				break;
341 			case media_raw_audio_format::B_AUDIO_SHORT:
342 				fCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;
343 				break;
344 			case media_raw_audio_format::B_AUDIO_UCHAR:
345 				fCodecContext->sample_fmt = AV_SAMPLE_FMT_U8;
346 				break;
347 
348 			case media_raw_audio_format::B_AUDIO_CHAR:
349 			default:
350 				return B_MEDIA_BAD_FORMAT;
351 				break;
352 		}
353 		if (fInputFormat.u.raw_audio.channel_mask == 0) {
354 			// guess the channel mask...
355 			switch (fInputFormat.u.raw_audio.channel_count) {
356 				default:
357 				case 2:
358 					fCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
359 					break;
360 				case 1:
361 					fCodecContext->channel_layout = AV_CH_LAYOUT_MONO;
362 					break;
363 				case 3:
364 					fCodecContext->channel_layout = AV_CH_LAYOUT_SURROUND;
365 					break;
366 				case 4:
367 					fCodecContext->channel_layout = AV_CH_LAYOUT_QUAD;
368 					break;
369 				case 5:
370 					fCodecContext->channel_layout = AV_CH_LAYOUT_5POINT0;
371 					break;
372 				case 6:
373 					fCodecContext->channel_layout = AV_CH_LAYOUT_5POINT1;
374 					break;
375 				case 8:
376 					fCodecContext->channel_layout = AV_CH_LAYOUT_7POINT1;
377 					break;
378 				case 10:
379 					fCodecContext->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE;
380 					break;
381 			}
382 		} else {
383 			// The bits match 1:1 for media_multi_channels and FFmpeg defines.
384 			fCodecContext->channel_layout = fInputFormat.u.raw_audio.channel_mask;
385 		}
386 	} else {
387 		TRACE("  UNSUPPORTED MEDIA TYPE!\n");
388 		return B_NOT_SUPPORTED;
389 	}
390 
391 	// TODO: Support letting the user overwrite this via
392 	// SetEncodeParameters(). See comments there...
393 	int wantedBitRate = (int)(rawBitRate / fBitRateScale
394 		* fEncodeParameters.quality);
395 	if (wantedBitRate == 0)
396 		wantedBitRate = (int)(rawBitRate / fBitRateScale);
397 
398 	fCodecContext->bit_rate = wantedBitRate;
399 
400 	if (fInputFormat.type == B_MEDIA_RAW_AUDIO) {
401 		// Some audio encoders support certain bitrates only. Use the
402 		// closest match to the wantedBitRate.
403 		const int kBitRates[] = {
404 			32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000,
405 			160000, 192000, 224000, 256000, 320000, 384000, 448000, 512000,
406 			576000, 640000
407 		};
408 		int diff = wantedBitRate;
409 		for (unsigned int i = 0; i < sizeof(kBitRates) / sizeof(int); i++) {
410 			int currentDiff = abs(wantedBitRate - kBitRates[i]);
411 			if (currentDiff < diff) {
412 				fCodecContext->bit_rate = kBitRates[i];
413 				diff = currentDiff;
414 			} else
415 				break;
416 		}
417 	}
418 
419 	TRACE("  rawBitRate: %d, wantedBitRate: %d (%.1f), context bitrate: %" PRId64 "\n",
420 		rawBitRate, wantedBitRate, fEncodeParameters.quality, fCodecContext->bit_rate);
421 
422 	// Add some known fixes from the FFmpeg API example:
423 	if (fCodecContext->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
424 		// Just for testing, we also add B frames */
425 		fCodecContext->max_b_frames = 2;
426 	} else if (fCodecContext->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
427 		// Needed to avoid using macroblocks in which some coeffs overflow.
428 		// This does not happen with normal video, it just happens here as
429 		// the motion of the chroma plane does not match the luma plane.
430 		fCodecContext->mb_decision = 2;
431 	}
432 
433 	// Unfortunately, we may fail later, when we try to open the codec
434 	// for real... but we need to delay this because we still allow
435 	// parameter/quality changes.
436 	return B_OK;
437 }
438 
439 
440 bool
441 AVCodecEncoder::_OpenCodecIfNeeded()
442 {
443 	if (fCodecInitStatus == CODEC_INIT_DONE)
444 		return true;
445 
446 	if (fCodecInitStatus == CODEC_INIT_FAILED)
447 		return false;
448 
449 	fCodecContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
450 
451 	// Some codecs need this to be set before open
452 	fFrame->format = fCodecContext->pix_fmt;
453 	fFrame->width = fCodecContext->width;
454 	fFrame->height = fCodecContext->height;
455 	av_frame_get_buffer(fFrame, 0);
456 
457 	// Open the codec
458 	int result = avcodec_open2(fCodecContext, fCodec, NULL);
459 	if (result >= 0)
460 		fCodecInitStatus = CODEC_INIT_DONE;
461 	else
462 		fCodecInitStatus = CODEC_INIT_FAILED;
463 
464 	TRACE("  avcodec_open(%p, %p): %d\n", fCodecContext, fCodec, result);
465 
466 	return fCodecInitStatus == CODEC_INIT_DONE;
467 
468 }
469 
470 
471 status_t
472 AVCodecEncoder::_EncodeAudio(const void* _buffer, int64 frameCount,
473 	media_encode_info* info)
474 {
475 	TRACE("AVCodecEncoder::_EncodeAudio(%p, %" B_PRId64 ", %p)\n", _buffer, frameCount, info);
476 
477 	if (fChunkBuffer == NULL)
478 		return B_NO_MEMORY;
479 
480 	status_t ret = B_OK;
481 
482 	const uint8* buffer = reinterpret_cast<const uint8*>(_buffer);
483 
484 	size_t inputSampleSize = fInputFormat.u.raw_audio.format
485 		& media_raw_audio_format::B_AUDIO_SIZE_MASK;
486 	size_t inputFrameSize = inputSampleSize
487 		* fInputFormat.u.raw_audio.channel_count;
488 
489 	size_t bufferSize = frameCount * inputFrameSize;
490 	bufferSize = min_c(bufferSize, kDefaultChunkBufferSize);
491 
492 	if (fCodecContext->frame_size > 1) {
493 		// Encoded audio. Things work differently from raw audio. We need
494 		// the fAudioFifo to pipe data.
495 		if (av_fifo_realloc2(fAudioFifo,
496 				av_fifo_size(fAudioFifo) + bufferSize) < 0) {
497 			TRACE("  av_fifo_realloc2() failed\n");
498             return B_NO_MEMORY;
499         }
500         av_fifo_generic_write(fAudioFifo, const_cast<uint8*>(buffer),
501         	bufferSize, NULL);
502 
503 		int frameBytes = fCodecContext->frame_size * inputFrameSize;
504 		uint8* tempBuffer = new(std::nothrow) uint8[frameBytes];
505 		if (tempBuffer == NULL)
506 			return B_NO_MEMORY;
507 
508 		// Encode as many chunks as can be read from the FIFO.
509 		while (av_fifo_size(fAudioFifo) >= frameBytes) {
510 			av_fifo_generic_read(fAudioFifo, tempBuffer, frameBytes, NULL);
511 
512 			ret = _EncodeAudio(tempBuffer, frameBytes, fCodecContext->frame_size,
513 				info);
514 			if (ret != B_OK)
515 				break;
516 		}
517 
518 		delete[] tempBuffer;
519 	} else {
520 		// Raw audio. The number of bytes returned from avcodec_encode_audio()
521 		// is always the same as the number of input bytes.
522 		return _EncodeAudio(buffer, bufferSize, frameCount,
523 			info);
524 	}
525 
526 	return ret;
527 }
528 
529 
530 status_t
531 AVCodecEncoder::_EncodeAudio(const uint8* buffer, size_t bufferSize,
532 	int64 frameCount, media_encode_info* info)
533 {
534 	status_t ret;
535 
536 	// Encode one audio chunk/frame.
537 	AVPacket* packet = av_packet_alloc();
538 	// By leaving these NULL, we let the encoder allocate memory as it needs.
539 	// This way we don't risk iving a too small buffer.
540 	packet->data = NULL;
541 	packet->size = 0;
542 
543 	int gotPacket = 0;
544 
545 	if (buffer) {
546 		av_frame_unref(fFrame);
547 		fFrame->nb_samples = frameCount;
548 
549 		int count = avcodec_fill_audio_frame(fFrame, fCodecContext->channels,
550 				fCodecContext->sample_fmt, (const uint8_t *) buffer, bufferSize, 1);
551 
552 		if (count < 0) {
553 			TRACE("  avcodec_encode_audio() failed filling data: %d\n", count);
554 			av_packet_free(&packet);
555 			return B_ERROR;
556 		}
557 
558 		/* Set the presentation time of the frame */
559 		fFrame->pts = (bigtime_t)(fFramesWritten * 1000000LL
560 			/ fInputFormat.u.raw_audio.frame_rate);
561 		fFramesWritten += fFrame->nb_samples;
562 
563 		ret = avcodec_send_frame(fCodecContext, fFrame);
564 		gotPacket = avcodec_receive_packet(fCodecContext, packet) == 0;
565 	} else {
566 		// If called with NULL, ask the encoder to flush any buffers it may
567 		// have pending.
568 		ret = avcodec_receive_packet(fCodecContext, packet);
569 		gotPacket = (ret == 0);
570 	}
571 
572 	if (buffer && fFrame->extended_data != fFrame->data)
573 		av_freep(&fFrame->extended_data);
574 
575 	if (ret != 0) {
576 		TRACE("  avcodec_encode_audio() failed: %s\n", strerror(ret));
577 		av_packet_free(&packet);
578 		return B_ERROR;
579 	}
580 
581 	fFramesWritten += frameCount;
582 
583 	if (gotPacket) {
584 		// Setup media_encode_info, most important is the time stamp.
585 		info->start_time = packet->pts;
586 
587 		if (packet->flags & AV_PKT_FLAG_KEY)
588 			info->flags = B_MEDIA_KEY_FRAME;
589 		else
590 			info->flags = 0;
591 
592 		// We got a packet out of the encoder, write it to the output stream
593 		ret = WriteChunk(packet->data, packet->size, info);
594 		if (ret != B_OK) {
595 			TRACE("  error writing chunk: %s\n", strerror(ret));
596 			av_packet_free(&packet);
597 			return ret;
598 		}
599 	}
600 
601 	av_packet_free(&packet);
602 	return B_OK;
603 }
604 
605 
606 status_t
607 AVCodecEncoder::_EncodeVideo(const void* buffer, int64 frameCount,
608 	media_encode_info* info)
609 {
610 	TRACE_IO("AVCodecEncoder::_EncodeVideo(%p, %lld, %p)\n", buffer, frameCount,
611 		info);
612 
613 	if (fChunkBuffer == NULL)
614 		return B_NO_MEMORY;
615 
616 	AVPacket* pkt = av_packet_alloc();
617 	while (frameCount > 0) {
618 		int bpr = fInputFormat.u.raw_video.display.bytes_per_row;
619 		size_t bufferSize = fInputFormat.u.raw_video.display.line_count * bpr;
620 
621 		// Run the pixel format conversion
622 		const uint8_t* buf8 = (const uint8_t*)buffer;
623 		sws_scale(fSwsContext, &buf8, &bpr, 0,
624 			fInputFormat.u.raw_video.display.line_count, fFrame->data,
625 			fFrame->linesize);
626 
627 		if (_EncodeVideoFrame(fFrame, pkt, info) == B_OK) {
628 			// Skip to the next frame (but usually, there is only one to encode
629 			// for video).
630 			frameCount--;
631 			fFramesWritten++;
632 			buffer = (const void*)((const uint8*)buffer + bufferSize);
633 		}
634 	}
635 
636 	// TODO: we should pass a NULL AVFrame and enter "draining" mode, then flush buffers
637 	// when we have finished and there is no more data. We cannot do that here, though, since
638 	// 1. It's not efficient
639 	// 2. It's incorrect, since many codecs need the "next" frame to be able to do optimization.
640 	// if we drain the codec, they cannot work with the "next" frame.
641 	//_EncodeVideoFrame(NULL, pkt, info);
642 	//avcodec_flush_buffers(fCodecContext);
643 	av_packet_free(&pkt);
644 	return B_OK;
645 }
646 
647 
648 status_t
649 AVCodecEncoder::_EncodeVideoFrame(AVFrame* frame, AVPacket* pkt, media_encode_info* info)
650 {
651 	// Encode one video chunk/frame.
652 	int result = avcodec_send_frame(fCodecContext, frame);
653 	if (result < 0) {
654 		TRACE("  avcodec_send_frame() failed: %d\n", result);
655 		return B_ERROR;
656 	}
657 
658 	// Increase the frame pts as in the ffmpeg sample code
659 	if (frame != NULL)
660 		frame->pts++;
661 
662 	while (result == 0) {
663 		result = avcodec_receive_packet(fCodecContext, pkt);
664 		if (result == 0) {
665 			TRACE("  avcodec_receive_packet: received one packet\n");
666 			// Maybe we need to use this PTS to calculate start_time:
667 			if (pkt->pts != AV_NOPTS_VALUE) {
668 				TRACE("  codec frame PTS: %" B_PRId64 " (codec time_base: %d/%d)\n",
669 					pkt->pts, fCodecContext->time_base.num,
670 					fCodecContext->time_base.den);
671 			} else {
672 				TRACE("  codec frame PTS: N/A (codec time_base: %d/%d)\n",
673 					fCodecContext->time_base.num, fCodecContext->time_base.den);
674 			}
675 
676 			// Setup media_encode_info, most important is the time stamp.
677 			info->start_time = (bigtime_t)(fFramesWritten * 1000000LL
678 				/ fInputFormat.u.raw_video.field_rate);
679 
680 			info->flags = 0;
681 			if (pkt->flags & AV_PKT_FLAG_KEY)
682 				info->flags |= B_MEDIA_KEY_FRAME;
683 
684 			// Write the chunk
685 			result = WriteChunk(pkt->data, pkt->size, info);
686 			if (result != B_OK) {
687 				TRACE("  error writing chunk: %s\n", strerror(result));
688 				break;
689 			}
690 		}
691 		av_packet_unref(pkt);
692 	}
693 	if (result == AVERROR(EAGAIN))
694 		return B_OK;
695 
696 	TRACE("   _EncodeVideoFrame(): returning...\n");
697 	return result;
698 }
699 
700