376 lines
7.6 KiB
C
376 lines
7.6 KiB
C
|
|
#include <Eina.h>
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavformat/avformat.h>
|
|
|
|
#include "ffmpeg.h"
|
|
|
|
FFmpeg *
|
|
ff_new(void)
|
|
{
|
|
FFmpeg *ff;
|
|
|
|
ff = (FFmpeg*)calloc(1, sizeof(FFmpeg));
|
|
if (!ff)
|
|
return NULL;
|
|
|
|
ff->videos = eina_array_new(1);
|
|
if (!ff->videos)
|
|
goto free_ff;
|
|
|
|
ff->audios = eina_array_new(1);
|
|
if (!ff->audios)
|
|
goto free_videos;
|
|
|
|
return ff;
|
|
|
|
free_videos:
|
|
eina_array_free(ff->videos);
|
|
free_ff:
|
|
free(ff);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
ff_free(FFmpeg *ff)
|
|
{
|
|
if (!ff)
|
|
return;
|
|
|
|
eina_array_free(ff->audios);
|
|
eina_array_free(ff->videos);
|
|
free(ff);
|
|
}
|
|
|
|
static void
|
|
_ff_free_lists(FFmpeg *ff)
|
|
{
|
|
Eina_Array_Iterator iterator;
|
|
void *p;
|
|
unsigned int i;
|
|
|
|
EINA_ARRAY_ITER_NEXT(ff->audios, i, p, iterator)
|
|
{
|
|
free(p);
|
|
}
|
|
eina_array_free(ff->audios);
|
|
|
|
EINA_ARRAY_ITER_NEXT(ff->videos, i, p, iterator)
|
|
{
|
|
free(p);
|
|
}
|
|
eina_array_free(ff->videos);
|
|
}
|
|
|
|
int
|
|
ff_file_open(FFmpeg *ff, const char *file)
|
|
{
|
|
FF_Video *video;
|
|
int err;
|
|
unsigned int i;
|
|
|
|
if ((!file || !*file))
|
|
return 0;
|
|
|
|
ff->format_ctx = avformat_alloc_context();
|
|
if (!ff->format_ctx)
|
|
{
|
|
printf("ff NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
ff->video_index = -1;
|
|
ff->audio_index = -1;
|
|
|
|
/* parse header of the file */
|
|
err = avformat_open_input(&ff->format_ctx, file, NULL, NULL);
|
|
if (err)
|
|
{
|
|
printf("avformat_open_input() failed");
|
|
goto free_format_ctx;
|
|
}
|
|
|
|
/* fill format_stx->streams */
|
|
if (avformat_find_stream_info(ff->format_ctx, NULL) < 0)
|
|
{
|
|
printf("avformat_find_stream_info() failed");
|
|
goto free_format_ctx;
|
|
}
|
|
|
|
printf(" * file name : %s\n", ff->format_ctx->url);
|
|
printf(" * format name : %s\n", ff->format_ctx->iformat->name);
|
|
printf(" * duration : %lld\n", ff->format_ctx->duration);
|
|
printf(" * bitrate : %lld\n", ff->format_ctx->bit_rate);
|
|
|
|
for (i = 0; i < ff->format_ctx->nb_streams; i++)
|
|
{
|
|
const AVCodecParameters *codec_par;
|
|
const AVCodec *codec;
|
|
|
|
printf(" * stream %d time base : %d/%d\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->time_base.num,
|
|
ff->format_ctx->streams[i]->time_base.den);
|
|
printf(" * stream %d r frame rate: %d/%d\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->r_frame_rate.num,
|
|
ff->format_ctx->streams[i]->r_frame_rate.den);
|
|
printf(" * stream %d a frame rate: %d/%d\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->avg_frame_rate.num,
|
|
ff->format_ctx->streams[i]->avg_frame_rate.den);
|
|
printf(" * stream %d start time: %lld\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->start_time);
|
|
printf(" * stream %d duration : %lld\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->duration);
|
|
printf(" * stream %d nb frames : %lld\n",
|
|
i,
|
|
ff->format_ctx->streams[i]->nb_frames);
|
|
|
|
codec_par = ff->format_ctx->streams[i]->codecpar;
|
|
codec = avcodec_find_decoder(codec_par->codec_id);
|
|
|
|
if (!codec)
|
|
{
|
|
printf("unsupported codec, continuing\n");
|
|
continue;
|
|
}
|
|
|
|
if (codec_par->codec_type == AVMEDIA_TYPE_VIDEO)
|
|
{
|
|
video = (FF_Video *)calloc(1, sizeof(FF_Video));
|
|
if (!video)
|
|
{
|
|
printf("fail to allocate memory for video\n");
|
|
continue;
|
|
}
|
|
|
|
video->codec_par = codec_par;
|
|
video->codec = codec;
|
|
video->stream_index = i;
|
|
eina_array_push(ff->videos, video);
|
|
|
|
if (ff->video_index == -1)
|
|
{
|
|
ff->video_index = 0;
|
|
}
|
|
}
|
|
else if (codec_par->codec_type == AVMEDIA_TYPE_AUDIO)
|
|
{
|
|
FF_Audio *audio;
|
|
|
|
audio = (FF_Audio *)calloc(1, sizeof(FF_Audio));
|
|
if (!audio)
|
|
{
|
|
printf("fail to allocate memory for audio\n");
|
|
continue;
|
|
}
|
|
|
|
audio->codec_par = codec_par;
|
|
audio->codec = codec;
|
|
audio->stream_index = i;
|
|
eina_array_push(ff->audios, audio);
|
|
|
|
if (ff->audio_index == -1)
|
|
{
|
|
ff->audio_index = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ff->video_index == -1)
|
|
{
|
|
printf("WARNING : no video stream`n");
|
|
}
|
|
|
|
video = (FF_Video *)eina_array_data_get(ff->videos, ff->video_index);
|
|
ff->codec_ctx = avcodec_alloc_context3(video->codec);
|
|
if (!ff->codec_ctx)
|
|
{
|
|
goto free_eina_lists;
|
|
}
|
|
|
|
if (avcodec_parameters_to_context(ff->codec_ctx, video->codec_par) < 0)
|
|
{
|
|
printf("WARNING : no video stream`n");
|
|
goto free_codec_ctx;
|
|
}
|
|
|
|
if (avcodec_open2(ff->codec_ctx, video->codec, NULL) < 0)
|
|
{
|
|
printf("failed to open codec through avcodec_open2\n");
|
|
goto free_codec_ctx;
|
|
}
|
|
|
|
return 1;
|
|
|
|
free_codec_ctx:
|
|
avcodec_free_context(&ff->codec_ctx);
|
|
free_eina_lists:
|
|
_ff_free_lists(ff);
|
|
free_format_ctx:
|
|
avformat_free_context(ff->format_ctx);
|
|
ff->audio_index = -1;
|
|
ff->video_index = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ff_file_close(FFmpeg *ff)
|
|
{
|
|
avcodec_free_context(&ff->codec_ctx);
|
|
_ff_free_lists(ff);
|
|
avformat_free_context(ff->format_ctx);
|
|
ff->audio_index = -1;
|
|
ff->video_index = -1;
|
|
}
|
|
|
|
void
|
|
ff_size_get(FFmpeg *ff, int *width, int *height)
|
|
{
|
|
FF_Video *v;
|
|
|
|
if (width) *width = 0;
|
|
if (height) *height = 0;
|
|
if (ff->video_index < 0)
|
|
return;
|
|
|
|
v = (FF_Video *)eina_array_data_get(ff->videos, ff->video_index);
|
|
|
|
if (width)
|
|
{
|
|
*width = ff->format_ctx->streams[v->stream_index]->codecpar->width;
|
|
}
|
|
|
|
if (height)
|
|
{
|
|
*height = ff->format_ctx->streams[v->stream_index]->codecpar->height;
|
|
}
|
|
}
|
|
|
|
double
|
|
ff_len_get(FFmpeg *ff)
|
|
{
|
|
/*
|
|
* get duration of the file, not of the video stream
|
|
* see https://stackoverflow.com/a/32538549
|
|
*/
|
|
return ff->format_ctx->duration / (double)AV_TIME_BASE;
|
|
}
|
|
|
|
int
|
|
_ff_fps_get(FFmpeg *ff, int *n, int *d)
|
|
{
|
|
FF_Video *v;
|
|
|
|
if (n) *n = 0;
|
|
if (d) *d = 0;
|
|
if (ff->video_index < 0)
|
|
return 0;
|
|
|
|
v = (FF_Video *)eina_array_data_get(ff->videos, ff->video_index);
|
|
|
|
if (n)
|
|
*n = ff->format_ctx->streams[v->stream_index]->avg_frame_rate.num;
|
|
|
|
if (d)
|
|
*d = ff->format_ctx->streams[v->stream_index]->avg_frame_rate.den;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
ff_fps_num_get(FFmpeg *ff)
|
|
{
|
|
int num;
|
|
|
|
_ff_fps_get(ff, &num, NULL);
|
|
|
|
return num;
|
|
}
|
|
|
|
int
|
|
ff_fps_den_get(FFmpeg *ff)
|
|
{
|
|
int den;
|
|
|
|
_ff_fps_get(ff, NULL, &den);
|
|
|
|
return den;
|
|
}
|
|
|
|
double
|
|
ff_fps_get(FFmpeg *ff)
|
|
{
|
|
int num, den;
|
|
|
|
if (!_ff_fps_get(ff, &num, &den))
|
|
return 0.0;
|
|
|
|
return (double)num / (double)den;
|
|
}
|
|
|
|
int
|
|
ff_video_handled(FFmpeg *ff)
|
|
{
|
|
return ff->video_index >= 0;
|
|
}
|
|
|
|
int
|
|
ff_audio_handled(FFmpeg *ff)
|
|
{
|
|
return ff->audio_index >= 0;
|
|
}
|
|
|
|
int
|
|
ff_format_get(FFmpeg *ff)
|
|
{
|
|
}
|
|
|
|
int
|
|
ff_video_channel_count(FFmpeg *ff)
|
|
{
|
|
return eina_array_count_get(ff->videos);
|
|
}
|
|
|
|
void
|
|
ff_video_channel_set(FFmpeg *ff, int channel)
|
|
{
|
|
if ((channel >= 0) || (channel < ff_video_channel_count(ff)))
|
|
{
|
|
ff->video_index = channel;
|
|
}
|
|
}
|
|
|
|
int
|
|
ff_video_channel_get(FFmpeg *ff)
|
|
{
|
|
return ff->video_index;
|
|
}
|
|
|
|
int
|
|
ff_audio_channel_count(FFmpeg *ff)
|
|
{
|
|
return eina_array_count_get(ff->audios);
|
|
}
|
|
|
|
void
|
|
ff_audio_channel_set(FFmpeg *ff, int channel)
|
|
{
|
|
if ((channel >= 0) || (channel < ff_audio_channel_count(ff)))
|
|
{
|
|
ff->audio_index = channel;
|
|
}
|
|
}
|
|
|
|
int
|
|
ff_audio_channel_get(FFmpeg *ff)
|
|
{
|
|
return ff->audio_index;
|
|
}
|