C# - ffmpeg(FFmpeg.AutoGen)를 이용해 오디오(mp2) 인코딩하는 예제(encode_audio.c)
지난 예제에 이어,
C# - ffmpeg(FFmpeg.AutoGen)를 이용해 멀티미디어 파일의 메타데이터를 보여주는 예제(metadata.c)
; https://www.sysnet.pe.kr/2/0/12936
이번에는
ffmpeg 예제 중 "
encode_audio.c" 파일을 FFmpeg.AutoGen으로 포팅하겠습니다.
using FFmpeg.AutoGen;
using FFmpeg.AutoGen.Example;
using System;
using System.IO;
namespace FFmpegApp1
{
internal unsafe class Program
{
static void Main(string[] args)
{
FFmpegBinariesHelper.RegisterFFmpegBinaries();
#if DEBUG
Console.WriteLine("Current directory: " + Environment.CurrentDirectory);
Console.WriteLine("Running in {0}-bit mode.", Environment.Is64BitProcess ? "64" : "32");
Console.WriteLine($"FFmpeg version info: {ffmpeg.av_version_info()}");
#endif
Console.WriteLine();
Console.WriteLine($"LIBAVFORMAT Version: {ffmpeg.LIBAVFORMAT_VERSION_MAJOR}.{ffmpeg.LIBAVFORMAT_VERSION_MINOR}");
encode_audio(@"C:\temp\output\test.mp2");
}
private static void encode_audio(string filename)
{
AVCodec* codec;
AVCodecContext* c = null;
AVFrame* frame = null;
AVPacket* pkt = null;
int i, j, k, ret;
short* samples;
float t, tincr;
/* find the MP2 encoder */
codec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_MP2);
if (codec == null)
{
Console.WriteLine("Codec not found");
return;
}
do
{
c = ffmpeg.avcodec_alloc_context3(codec);
if (c == null)
{
Console.WriteLine("Could not allocate audio codec context");
break;
}
/* put sample parameters */
c->bit_rate = 64000;
/* check that the encoder supports s16 pcm input */
c->sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16;
if (check_sample_fmt(codec, c->sample_fmt) == 0)
{
Console.WriteLine($"Encoder does not support sample format {ffmpeg.av_get_sample_fmt_name(c->sample_fmt)}");
break;
}
/* select other audio parameters supported by the encoder */
c->sample_rate = select_sample_rate(codec);
c->channel_layout = select_channel_layout(codec);
c->channels = ffmpeg.av_get_channel_layout_nb_channels(c->channel_layout);
/* open it */
if (ffmpeg.avcodec_open2(c, codec, null) < 0)
{
Console.WriteLine("Could not open codec");
break;
}
using FileStream f = System.IO.File.OpenWrite(filename);
/* packet for holding encoded output */
pkt = ffmpeg.av_packet_alloc();
if (pkt == null)
{
Console.WriteLine( "could not allocate the packet");
break;
}
/* frame containing input raw audio */
frame = ffmpeg.av_frame_alloc();
if (frame == null)
{
Console.WriteLine("Could not allocate audio frame");
break;
}
frame->nb_samples = c->frame_size;
frame->format = (int)c->sample_fmt;
frame->channel_layout = c->channel_layout;
/* allocate the data buffers */
ret = ffmpeg.av_frame_get_buffer(frame, 0);
if (ret < 0)
{
Console.WriteLine("Could not allocate audio data buffers");
break;
}
/* encode a single tone sound */
t = 0;
tincr = (float)(2 * Math.PI * 440.0 / c->sample_rate);
for (i = 0; i < 200; i++)
{
/* make sure the frame is writable -- makes a copy if the encoder
* kept a reference internally */
ret = ffmpeg.av_frame_make_writable(frame);
if (ret < 0)
{
break;
}
samples = (short *)frame->data[0];
for (j = 0; j < c->frame_size; j++)
{
samples[2 * j] = (short)(Math.Sin(t) * 10000);
for (k = 1; k < c->channels; k++)
{
samples[2 * j + k] = samples[2 * j];
}
t += tincr;
}
encode(c, frame, pkt, f);
}
/* flush the encoder */
encode(c, null, pkt, f);
} while (false);
if (frame != null)
{
ffmpeg.av_frame_free(&frame);
}
if (pkt != null)
{
ffmpeg.av_packet_free(&pkt);
}
if (c != null)
{
ffmpeg.avcodec_free_context(&c);
}
}
/* check that a given sample format is supported by the encoder */
static unsafe int check_sample_fmt(AVCodec* codec, AVSampleFormat sample_fmt)
{
AVSampleFormat* p = codec->sample_fmts;
while (*p != AVSampleFormat.AV_SAMPLE_FMT_NONE)
{
if (*p == sample_fmt)
{
return 1;
}
p++;
}
return 0;
}
/* just pick the highest supported samplerate */
static unsafe int select_sample_rate(AVCodec* codec)
{
int* p;
int best_samplerate = 0;
if (codec->supported_samplerates == null)
{
return 44100;
}
p = codec->supported_samplerates;
while (*p != 0)
{
if (best_samplerate == 0 || Math.Abs(44100 - *p) < Math.Abs(44100 - best_samplerate))
{
best_samplerate = *p;
}
p++;
}
return best_samplerate;
}
/* select layout with the highest channel count */
static unsafe ulong select_channel_layout(AVCodec* codec)
{
ulong* p;
ulong best_ch_layout = 0;
int best_nb_channels = 0;
if (codec->channel_layouts == null)
{
return ffmpeg.AV_CH_LAYOUT_STEREO;
}
p = codec->channel_layouts;
while (*p != 0)
{
int nb_channels = ffmpeg.av_get_channel_layout_nb_channels(*p);
if (nb_channels > best_nb_channels)
{
best_ch_layout = *p;
best_nb_channels = nb_channels;
}
p++;
}
return best_ch_layout;
}
static bool encode(AVCodecContext* ctx, AVFrame* frame, AVPacket* pkt, FileStream output)
{
int ret;
/* send the frame for encoding */
ret = ffmpeg.avcodec_send_frame(ctx, frame);
if (ret < 0)
{
Console.WriteLine("Error sending the frame to the encoder");
return false;
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0)
{
ret = ffmpeg.avcodec_receive_packet(ctx, pkt);
if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
{
return true;
}
else if (ret < 0)
{
Console.WriteLine("Error encoding audio frame");
return false;
}
ReadOnlySpan<byte> buffer = new ReadOnlySpan<byte>(pkt->data, pkt->size);
output.Write(buffer);
ffmpeg.av_packet_unref(pkt);
}
return true;
}
}
}
위의 코드를 실행하면 mp2 포맷의 오디오 파일이 생성됩니다. 그 파일의 포맷을 검사하면,
C:\temp\output> ffprobe test.mp2
ffprobe version 4.4.1 Copyright (c) 2007-2021 the FFmpeg developers
built with Microsoft (R) C/C++ Optimizing Compiler Version 19.30.30705 for x64
configuration: ...[생략]... --extra-cxxflags=-MD
libavutil 56. 70.100 / 56. 70.100
...[생략]...
libpostproc 55. 9.100 / 55. 9.100
[mp3 @ 0000022DD9812140] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from 'test.mp2':
Duration: 00:00:05.22, start: 0.000000, bitrate: 64 kb/s
Stream #0:0: Audio: mp2, 44100 Hz, stereo, fltp, 64 kb/s
mp2라고 나오고, 실제로 미디어 플레이어를 이용해 재생하면 "뚜~~~" 소리가 5.22초 동안 재생됩니다.
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
(
이 글의 소스 코드는 github에 올려져 있습니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]