Reputation: 31
I am working on to record audio using ALSA library. I am able to record the audio using the same library in .wav file, but what I need is to record an .mp4
file. For that I initialize the FFmpeg encoder to create MP4 file and trying to record the audio by writing the audio frames into the file. The result which I am getting is an empty MP4 file with no audio.
Here I am attaching the code which I have tried
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <alsa/asoundlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
int terminate = 0;
int channels = 2;
// Function to handle termination signal
void sigint_handler(int sig) {
terminate = 1;
}
// Function to initialize the FFmpeg encoder and writer
AVFormatContext* init_ffmpeg_writer(const char *filename, AVCodecContext **audio_codec_ctx) {
AVFormatContext *fmt_ctx = NULL;
AVCodec *audio_codec = NULL;
AVStream *audio_stream = NULL;
// Initialize the output format context
if (avformat_alloc_output_context2(&fmt_ctx, NULL, "mp4", filename) < 0) {
fprintf(stderr, "Could not create output context\n");
exit(1);
}
// Find the codec
audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!audio_codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
// Create a new stream
audio_stream = avformat_new_stream(fmt_ctx, audio_codec);
if (!audio_stream) {
fprintf(stderr, "Could not create audio stream\n");
exit(1);
}
// Set up codec context
*audio_codec_ctx = avcodec_alloc_context3(audio_codec);
(*audio_codec_ctx)->channels = 2;
(*audio_codec_ctx)->channel_layout = AV_CH_LAYOUT_STEREO;
(*audio_codec_ctx)->sample_rate = 44100;
(*audio_codec_ctx)->sample_fmt = AV_SAMPLE_FMT_FLTP; // 32-bit float for input format
(*audio_codec_ctx)->bit_rate = 128000; // Bitrate for AAC encoding
// Open the codec
if (avcodec_open2(*audio_codec_ctx, audio_codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
// Copy codec parameters from codec context to the stream
if (avcodec_parameters_from_context(audio_stream->codecpar, *audio_codec_ctx) < 0) {
fprintf(stderr, "Could not copy codec parameters\n");
exit(1);
}
// Open the output file
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&fmt_ctx->pb, filename, AVIO_FLAG_WRITE) < 0) {
fprintf(stderr, "Could not open output file\n");
exit(1);
}
}
// Write the file header
if (avformat_write_header(fmt_ctx, NULL) < 0) {
fprintf(stderr, "Error occurred when writing header\n");
exit(1);
}
return fmt_ctx;
}
void write_audio_frame(AVFormatContext *fmt_ctx, AVCodecContext *audio_codec_ctx, uint8_t *buffer, int buffer_size) {
AVPacket pkt;
AVFrame *frame;
int ret;
static int64_t frame_count = 0; // Ensure this is initialized correctly
static double stream_time = 0;
// Initialize the packet
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
// Allocate and set up frame
frame = av_frame_alloc();
frame->nb_samples = audio_codec_ctx->frame_size;
frame->channel_layout = audio_codec_ctx->channel_layout;
frame->format = audio_codec_ctx->sample_fmt;
frame->sample_rate = audio_codec_ctx->sample_rate;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate frame buffer\n");
exit(1);
}
// Initialize swresample context
SwrContext *swr_ctx = swr_alloc();
av_opt_set_int(swr_ctx, "in_channel_layout", frame->channel_layout, 0);
av_opt_set_int(swr_ctx, "out_channel_layout", frame->channel_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", 44100, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", 44100, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
if (swr_init(swr_ctx) < 0) {
fprintf(stderr, "Error initializing swresample context\n");
exit(1);
}
// Calculate the number of samples based on buffer size and format
int num_samples = buffer_size / (2 * channels); // 2 bytes per sample (S16)
uint8_t *out_buffer = (uint8_t *)malloc(num_samples * 4); // 4 bytes per sample (float)
// Resample audio data
ret = swr_convert(swr_ctx, &out_buffer, num_samples, (const uint8_t **)&buffer, num_samples);
if (ret < 0) {
fprintf(stderr, "Error during resampling\n");
exit(1);
}
// Copy resampled data to the frame's buffer
int out_size = num_samples * av_get_bytes_per_sample(audio_codec_ctx->sample_fmt);
memcpy(frame->data[0], out_buffer, out_size);
if (frame->data[0] == NULL) {
fprintf(stderr, "Frame data is NULL\n");
}
// Set timestamps for the packet
pkt.pts = pkt.dts = (frame_count * audio_codec_ctx->frame_size * AV_TIME_BASE) / audio_codec_ctx->sample_rate;
stream_time += (double)frame->nb_samples / audio_codec_ctx->sample_rate;
// Send the frame for encoding
ret = avcodec_send_frame(audio_codec_ctx, frame);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
// Encoder is temporarily unavailable, wait or retry
fprintf(stderr, "Encoder temporarily unavailable, retrying...\n");
return;
} else {
// Another error occurred
fprintf(stderr, "Error sending audio frame to encoder: %s\n", av_err2str(ret));
exit(1);
}
}
// Receive the encoded packet
ret = avcodec_receive_packet(audio_codec_ctx, &pkt);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
// No packet is available yet, maybe retry later
fprintf(stderr, "No packet available, retrying...\n");
return;
} else {
fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
exit(1);
}
}
pkt.stream_index = 0;
// Write the packet to the output
ret = av_interleaved_write_frame(fmt_ctx, &pkt);
if (ret < 0) {
fprintf(stderr, "Error while writing frame\n");
exit(1);
}else if (ret==0){
printf("Writing frames successfully\n");
}
// Clean up
av_frame_free(&frame);
av_packet_unref(&pkt);
free(out_buffer);
frame_count++; // Increment the frame count to track timestamps
}
int main() {
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
int err;
unsigned int sample_rate = 44100;
snd_pcm_uframes_t frames = 32;
char *buffer;
int buffer_size;
// Register signal handler for termination (Ctrl+C)
signal(SIGINT, sigint_handler);
// Open the PCM device for recording (capture)
if ((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf(stderr, "cannot open audio device %s (%s)\n", "default", snd_strerror(err));
exit(1);
}
// Allocate the hardware parameters structure
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
exit(1);
}
// Initialize the hardware parameters with default values
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) {
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
exit(1);
}
// Set the desired hardware parameters
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &sample_rate, 0)) < 0) {
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0) {
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
exit(1);
}
// Free the hardware parameters structure
snd_pcm_hw_params_free(hw_params);
// Prepare the PCM device for use
if ((err = snd_pcm_prepare(capture_handle)) < 0) {
fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
exit(1);
}
// Calculate buffer size
buffer_size = frames * channels * 2; // 2 bytes/sample, 2 channels
buffer = (char *) malloc(buffer_size);
// Initialize FFmpeg
av_register_all();
// Initialize the output file and codec
AVCodecContext *audio_codec_ctx = NULL;
AVFormatContext *fmt_ctx = init_ffmpeg_writer("recorded_audio.mp4", &audio_codec_ctx);
printf("Recording...\n");
// Record audio data until termination signal is received
while (!terminate) {
printf("entered while\n");
if ((err = snd_pcm_readi(capture_handle, buffer, frames)) != frames) {
fprintf(stderr, "read from audio interface failed (%s)\n", snd_strerror(err));
exit(1);
}
// Write audio frame to the MP4 file
write_audio_frame(fmt_ctx, audio_codec_ctx, (uint8_t *)buffer, buffer_size);
}
printf("Recording finished.\n");
// Write the file footer and close
av_write_trailer(fmt_ctx);
avcodec_free_context(&audio_codec_ctx);
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
// Clean up ALSA resources
snd_pcm_close(capture_handle);
free(buffer);
return 0;
}
Here I am attaching the logs too
Recording...
entered while
No packet available, retrying...
entered while
[mp4 @ 0x611490ddeb40] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
[mp4 @ 0x611490ddeb40] Encoder did not produce proper pts, making some up.
Writing frames successfully
entered while
Writing frames successfully
entered while
Writing frames successfully
entered while
Writing frames successfully
Can anyone help me how to resolve the above error by setting up the timestamp properly and record audio in mp4 file using ALSA .
Upvotes: 2
Views: 33