Created
August 18, 2018 17:22
-
-
Save AlexVestin/15b90d72f51ff7521cd7ce4b70056dff to your computer and use it in GitHub Desktop.
Write to in memory buffert (avio_write.c example)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "avio_write.h" | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libavformat/avio.h> | |
#include <libavutil/imgutils.h> | |
#include <libswresample/swresample.h> | |
#include <libavutil/opt.h> | |
struct buffer_data { | |
uint8_t *buf; | |
int size; | |
uint8_t *ptr; | |
size_t room; ///< size left in the buffer | |
}; | |
AVFormatContext *ofmt_ctx = NULL; | |
AVIOContext *avio_ctx = NULL; | |
uint8_t *avio_ctx_buffer = NULL; | |
size_t avio_ctx_buffer_size = 4096; | |
int i, ret = 0; | |
struct buffer_data bd = { 0 }; | |
const size_t bd_buf_size = 1024; | |
const char* codec_name = "libvpx"; | |
//VIDEO | |
AVFrame *video_frame, *audio_frame; | |
int frame_idx; | |
AVStream *video_stream = NULL; | |
AVStream *audio_stream = NULL; | |
AVPacket *pkt; | |
static struct SwsContext *audio_swr_ctx = NULL; | |
AVCodecContext *video_ctx, *audio_ctx; | |
const int NR_COLORS = 4; | |
int have_audio = 0, have_video = 0; | |
//AUDIO | |
int frame_bytes, audio_idx, dst_nb_samples; | |
int src_sample_rate, src_bit_rate, src_nr_channels, src_size; | |
uint8_t* src_buf_left; | |
uint8_t* src_buf_right; | |
float* audio_buffer_left; | |
float* audio_buffer_right; | |
int audio_buffer_size, max_audio_buffer_size; | |
static int64_t seek (void *opaque, int64_t offset, int whence) { | |
struct buffer_data *bd = (struct buffer_data *)opaque; | |
switch(whence){ | |
case SEEK_SET: | |
bd->ptr = bd->buf + offset; | |
return bd->ptr; | |
break; | |
case SEEK_CUR: | |
bd->ptr += offset; | |
break; | |
case SEEK_END: | |
bd->ptr = (bd->buf + bd->size) + offset; | |
return bd->ptr; | |
break; | |
case AVSEEK_SIZE: | |
return bd->size; | |
break; | |
default: | |
return -1; | |
} | |
return 1; | |
} | |
static int write_packet(void *opaque, uint8_t *buf, int buf_size) { | |
struct buffer_data *bd = (struct buffer_data *)opaque; | |
while (buf_size > bd->room) { | |
int64_t offset = bd->ptr - bd->buf; | |
bd->buf = av_realloc_f(bd->buf, 2, bd->size); | |
if (!bd->buf) | |
return AVERROR(ENOMEM); | |
bd->size *= 2; | |
bd->ptr = bd->buf + offset; | |
bd->room = bd->size - offset; | |
} | |
//printf("write packet pkt_size:%d used_buf_size:%zu buf_size:%zu buf_room:%zu\n", buf_size, bd->ptr-bd->buf, bd->size, bd->room); | |
memcpy(bd->ptr, buf, buf_size); | |
bd->ptr += buf_size; | |
bd->room -= buf_size; | |
//free(buf); | |
return buf_size; | |
} | |
static void encode(AVFrame *frame, AVCodecContext* cod, AVStream* out, AVPacket* p) { | |
ret = avcodec_send_frame(cod, frame); | |
if (ret < 0) { | |
//printf(stderr, "Error sending a frame for encoding\n"); | |
exit(1); | |
} | |
while (ret >= 0) { | |
ret = avcodec_receive_packet(cod, p); | |
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){ | |
av_packet_unref(p); | |
return; | |
} | |
else if (ret < 0) { | |
//printf(stderr, "Error during encoding\n"); | |
exit(1); | |
} | |
//log_packet(ofmt_ctx, pkt, "write"); | |
p->stream_index = out->index; | |
av_packet_rescale_ts(p, cod->time_base, out->time_base); | |
av_write_frame(ofmt_ctx, p); | |
av_packet_unref(p); | |
} | |
} | |
void flip_vertically(uint8_t *pixels) { | |
const size_t width = video_ctx->width; | |
const size_t height = video_ctx->height; | |
const size_t stride = width * NR_COLORS; | |
uint8_t *row = malloc(stride); | |
uint8_t *low = pixels; | |
uint8_t *high = &pixels[(height - 1) * stride]; | |
for (; low < high; low += stride, high -= stride) { | |
memcpy(row, low, stride); | |
memcpy(low, high, stride); | |
memcpy(high, row, stride); | |
} | |
free(row); | |
} | |
void rgb2yuv420p(uint8_t *destination, uint8_t *rgb, size_t width, size_t height) | |
{ | |
size_t image_size = width * height; | |
size_t upos = image_size; | |
size_t vpos = upos + upos / 4; | |
size_t i = 0; | |
uint8_t r, g, b; | |
size_t idx; | |
for( size_t line = 0; line < height; ++line ) { | |
if( !(line % 2) ) { | |
for( size_t x = 0; x < width; x += 2 ) | |
{ | |
r = rgb[NR_COLORS * i]; | |
g = rgb[NR_COLORS * i + 1]; | |
b = rgb[NR_COLORS * i + 2]; | |
destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; | |
destination[upos++] = ((-38*r + -74*g + 112*b) >> 8) + 128; | |
destination[vpos++] = ((112*r + -94*g + -18*b) >> 8) + 128; | |
r = rgb[NR_COLORS * i]; | |
g = rgb[NR_COLORS * i + 1]; | |
b = rgb[NR_COLORS * i + 2]; | |
destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; | |
} | |
} | |
else | |
{ | |
for( size_t x = 0; x < width; x += 1 ) | |
{ | |
r = rgb[NR_COLORS * i]; | |
g = rgb[NR_COLORS * i + 1]; | |
b = rgb[NR_COLORS * i + 2]; | |
destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; | |
} | |
} | |
} | |
} | |
void add_video_frame(uint8_t* frame){ | |
flip_vertically(frame); | |
ret = av_frame_make_writable(video_frame); | |
// ~15% faster than sws_scale | |
int size = (video_ctx->width * video_ctx->height * 3) / 2; | |
uint8_t* yuv_buffer = malloc(size); | |
rgb2yuv420p(yuv_buffer, frame, video_ctx->width, video_ctx->height); | |
av_image_fill_arrays ( | |
(AVPicture*)video_frame->data, | |
video_frame->linesize, | |
yuv_buffer, | |
video_frame->format, | |
video_frame->width, | |
video_frame->height, | |
1 | |
); | |
video_frame->pts = frame_idx++; | |
encode(video_frame, video_ctx, video_stream, pkt); | |
free(yuv_buffer); | |
} | |
void write_header() { | |
ret = avformat_write_header(ofmt_ctx, NULL); | |
if (ret < 0) { | |
//printf(stderr, "Error occurred: %s\n", av_err2str(ret)); | |
//printf(stderr, "Error occurred when opening output file\n"); | |
exit(1); | |
} | |
} | |
void open_video(int w, int h, int fps, int br, int preset_idx, int codec_idx, int format_idx){ | |
const char* formats[] = {"webm", "mp4", "mp3", "aac", "ogg" }; | |
const char* codecs[] = { "libvpx", "libx264" }; | |
AVOutputFormat* of = av_guess_format(formats[format_idx], 0, 0); | |
bd.ptr = bd.buf = av_malloc(bd_buf_size); | |
if (!bd.buf) { | |
ret = AVERROR(ENOMEM); | |
} | |
bd.size = bd.room = bd_buf_size; | |
avio_ctx_buffer = av_malloc(avio_ctx_buffer_size); | |
if (!avio_ctx_buffer) { | |
ret = AVERROR(ENOMEM); | |
exit(1); | |
} | |
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 1, &bd, NULL, &write_packet, &seek); | |
if (!avio_ctx) { | |
ret = AVERROR(ENOMEM); | |
exit(1); | |
} | |
ret = avformat_alloc_output_context2(&ofmt_ctx, of, NULL, NULL); | |
if (ret < 0) { | |
//printf(stderr, "Could not create output context\n"); | |
exit(1); | |
} | |
AVCodec* video_codec = avcodec_find_encoder_by_name(codecs[codec_idx]); | |
if (!video_codec) { | |
//printf(stderr, "Codec '%s' not found\n", codec_name); | |
exit(1); | |
} | |
video_ctx = avcodec_alloc_context3(video_codec); | |
video_ctx->width = w; | |
video_ctx->height = h; | |
video_ctx->time_base.num = 1; | |
video_ctx->time_base.den = fps; | |
video_ctx->bit_rate = br; | |
video_ctx->gop_size = 10; | |
video_ctx->max_b_frames = 1; | |
video_ctx->pix_fmt = AV_PIX_FMT_YUV420P; | |
const char *presets[] = { "ultrafast", "veryfast", "fast", "medium", "slow", "veryslow" }; | |
av_opt_set(video_ctx->priv_data, "preset", presets[preset_idx], 0); | |
if(avcodec_open2(video_ctx, video_codec, NULL) < 0) { | |
//printf("couldnt open codec\n"); | |
exit(1); | |
} | |
// Frame initalization | |
video_frame = av_frame_alloc(); | |
video_frame->format = video_ctx->pix_fmt; | |
video_frame->width = w; | |
video_frame->height = h; | |
ret = av_frame_get_buffer(video_frame, 0); | |
pkt = av_packet_alloc(); | |
if(!pkt){ | |
//printf("errror packer\n"); | |
exit(1); | |
} | |
video_stream = avformat_new_stream(ofmt_ctx, NULL); | |
if(!video_stream){ | |
//printf(stderr, "error making stream\n"); | |
exit(1); | |
} | |
ofmt_ctx->pb = avio_ctx; | |
ofmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO; | |
ofmt_ctx->oformat = of; | |
video_stream->codec->codec_tag = 0; | |
video_stream->time_base = video_ctx->time_base; | |
video_stream->id = ofmt_ctx->nb_streams-1; | |
ret = avcodec_parameters_from_context(video_stream->codecpar, video_ctx); | |
frame_idx = 0; | |
have_video = 1; | |
} | |
uint8_t* close_stream(int* size) { | |
if(have_video)encode(NULL, video_ctx, video_stream, pkt); | |
if(have_audio)encode(NULL, audio_ctx, audio_stream, pkt); | |
av_write_trailer(ofmt_ctx); | |
avformat_free_context(ofmt_ctx); | |
if (have_video) { | |
avcodec_free_context(&video_ctx); | |
av_frame_free(&video_frame); | |
} | |
if (have_audio) { | |
avcodec_free_context(&audio_ctx); | |
av_frame_free(&audio_frame); | |
swr_free(&audio_swr_ctx); | |
free(audio_buffer_right); | |
free(audio_buffer_left); | |
} | |
av_freep(&avio_ctx->buffer); | |
av_free(avio_ctx); | |
*size = bd.size - bd.room; | |
return bd.buf; | |
} | |
void free_buffer(){ | |
av_free(bd.buf); | |
} | |
static AVFrame *alloc_audio_frame() { | |
AVFrame *audio_frame = av_frame_alloc(); | |
int ret; | |
if (!audio_frame) { | |
//printf(stderr, "Error allocating an audio frame\n"); | |
exit(1); | |
} | |
audio_frame->format = audio_ctx->sample_fmt; | |
audio_frame->channel_layout = audio_ctx->channel_layout; | |
audio_frame->sample_rate = audio_ctx->sample_rate; | |
audio_frame->nb_samples = audio_ctx->frame_size; | |
ret = av_frame_get_buffer(audio_frame, 4); | |
if (ret < 0) { | |
//printf(stderr, "Error allocating an audio buffer\n"); | |
exit(1); | |
} | |
return audio_frame; | |
} | |
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt){ | |
const enum AVSampleFormat *p = codec->sample_fmts; | |
while (*p != AV_SAMPLE_FMT_NONE) { | |
if (*p == sample_fmt) | |
return 1; | |
p++; | |
} | |
return 0; | |
} | |
void add_audio_frame(float* left, float* right, int size) { | |
if(size + audio_buffer_size > max_audio_buffer_size) { | |
audio_buffer_left = realloc(audio_buffer_left, (size + audio_buffer_size) * 4); | |
audio_buffer_right = realloc(audio_buffer_right, (size + audio_buffer_size) * 4); | |
} | |
memcpy(audio_buffer_left + audio_buffer_size, left, size * sizeof(float)); | |
memcpy(audio_buffer_right + audio_buffer_size, right, size * sizeof(float)); | |
audio_buffer_size += size; | |
int count = 0, ret; | |
while(audio_buffer_size > audio_frame->nb_samples) { | |
ret = av_frame_make_writable(audio_frame); | |
if(ret < 0){ | |
printf("error\n"); | |
exit(1); | |
} | |
audio_frame->data[0] = audio_buffer_left; | |
audio_frame->data[1] = audio_buffer_right; | |
dst_nb_samples = av_rescale_rnd ( | |
swr_get_delay(audio_swr_ctx, audio_ctx->sample_rate) + audio_frame->nb_samples, | |
src_sample_rate, | |
audio_ctx->sample_rate, | |
AV_ROUND_UP | |
); | |
ret = swr_convert ( | |
audio_swr_ctx, | |
audio_frame->data, | |
dst_nb_samples, | |
(const uint8_t **)audio_frame->data, | |
audio_frame->nb_samples | |
); | |
if(ret < 0){ | |
printf("error converting \n"); | |
exit(1); | |
} | |
audio_frame->pts = av_rescale_q( | |
frame_bytes, | |
(AVRational){1, audio_ctx->sample_rate}, | |
audio_ctx->time_base | |
); | |
frame_bytes += dst_nb_samples; | |
encode(audio_frame, audio_ctx, audio_stream, pkt); | |
//Shift array buffer | |
memcpy(audio_buffer_left, audio_buffer_left + audio_frame->nb_samples, audio_buffer_size*sizeof(float)); | |
memcpy(audio_buffer_right, audio_buffer_right + audio_frame->nb_samples, audio_buffer_size*sizeof(float)); | |
audio_buffer_size -= audio_frame->nb_samples; | |
} | |
} | |
void open_audio(int sample_rate, int nr_channels, int bit_rate, int codec_idx) { | |
src_sample_rate = sample_rate; | |
src_bit_rate = bit_rate; | |
src_nr_channels = nr_channels; | |
const int codecs[] = { AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC, AV_CODEC_ID_MP3 }; | |
AVCodec* ac = avcodec_find_encoder(codecs[codec_idx]); | |
if(!ac) { | |
printf("error making audio codec context\n"); | |
exit(-1); | |
} | |
audio_stream = avformat_new_stream(ofmt_ctx, NULL); | |
audio_stream->id = ofmt_ctx->nb_streams-1; | |
audio_ctx = avcodec_alloc_context3(ac); | |
audio_ctx->bit_rate = bit_rate; | |
audio_ctx->sample_rate = sample_rate; | |
if (ac->supported_samplerates) { | |
audio_ctx->sample_rate = ac->supported_samplerates[0]; | |
for (i = 0; ac->supported_samplerates[i]; i++) { | |
if (ac->supported_samplerates[i] == sample_rate) | |
audio_ctx->sample_rate = sample_rate; | |
} | |
} | |
audio_ctx->channels = nr_channels; | |
audio_ctx->channel_layout = AV_CH_LAYOUT_STEREO; | |
if (ac->channel_layouts) { | |
audio_ctx->channel_layout = ac->channel_layouts[0]; | |
for (i = 0; ac->channel_layouts[i]; i++) { | |
if (ac->channel_layouts[i] == AV_CH_LAYOUT_STEREO) | |
audio_ctx->channel_layout = AV_CH_LAYOUT_STEREO; | |
} | |
} | |
audio_ctx->channels = av_get_channel_layout_nb_channels(audio_ctx->channel_layout); | |
audio_ctx->sample_fmt = ac->sample_fmts[0]; | |
audio_stream->time_base = (AVRational){1, audio_ctx->sample_rate}; | |
ret = avcodec_open2(audio_ctx, ac, NULL); | |
if (ret < 0) { | |
printf(stderr, "Could not open audio codec: %s\n", av_err2str(ret)); | |
exit(1); | |
} | |
audio_frame = alloc_audio_frame(); | |
ret = avcodec_parameters_from_context(audio_stream->codecpar, audio_ctx); | |
if (ret < 0) { | |
printf(stderr, "Could not copy the stream parameters\n"); | |
exit(1); | |
} | |
audio_swr_ctx = swr_alloc(); | |
if (!audio_swr_ctx) { | |
printf(stderr, "Could not allocate resampler context\n"); | |
exit(1); | |
} | |
av_opt_set_int (audio_swr_ctx, "in_channel_count", nr_channels, 0); | |
av_opt_set_int (audio_swr_ctx, "in_sample_rate", sample_rate, 0); | |
av_opt_set_sample_fmt(audio_swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); | |
av_opt_set_int (audio_swr_ctx, "out_channel_count", audio_ctx->channels, 0); | |
av_opt_set_int (audio_swr_ctx, "out_sample_rate", audio_ctx->sample_rate, 0); | |
av_opt_set_sample_fmt(audio_swr_ctx, "out_sample_fmt", audio_ctx->sample_fmt, 0); | |
if ((ret = swr_init(audio_swr_ctx)) < 0) { | |
printf(stderr, "Failed to initialize the resampling context\n"); | |
exit(1); | |
} | |
if(!pkt)pkt = av_packet_alloc(); | |
if(!pkt){ | |
exit(1); | |
} | |
audio_buffer_size = frame_bytes = 0; | |
audio_buffer_left = malloc(sample_rate * 4); | |
audio_buffer_right = malloc(sample_rate * 4); | |
max_audio_buffer_size = sample_rate; | |
have_audio = 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thx for the example :)