This source file includes following definitions.
- dc_video_decoder_open
- dc_video_decoder_read
- dc_video_decoder_close
#include "video_decoder.h"
#include <time.h>
#include <gpac/network.h>
int dc_video_decoder_open(VideoInputFile *video_input_file, VideoDataConf *video_data_conf, int mode, int no_loop, int nb_consumers)
{
s32 ret;
u32 i;
s32 open_res;
AVInputFormat *in_fmt = NULL;
AVDictionary *options = NULL;
AVCodecContext *codec_ctx;
AVCodec *codec;
memset(video_input_file, 0, sizeof(VideoInputFile));
if (video_data_conf->width > 0 && video_data_conf->height > 0) {
char vres[16];
snprintf(vres, sizeof(vres), "%dx%d", video_data_conf->width, video_data_conf->height);
ret = av_dict_set(&options, "video_size", vres, 0);
if (ret < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Could not set video size %s.\n", vres));
return -1;
}
}
if (video_data_conf->framerate > 0) {
char vfr[16];
snprintf(vfr, sizeof(vfr), "%d", video_data_conf->framerate);
ret = av_dict_set(&options, "framerate", vfr, 0);
if (ret < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Could not set video framerate %s.\n", vfr));
return -1;
}
}
if (strlen(video_data_conf->pixel_format)) {
ret = av_dict_set(&options, "pixel_format", video_data_conf->pixel_format, 0);
if (ret < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Could not set pixel format %s.\n", video_data_conf->pixel_format));
return -1;
}
}
#ifndef WIN32
if (strcmp(video_data_conf->v4l2f, "") != 0) {
ret = av_dict_set(&options, "input_format", video_data_conf->v4l2f, 0);
if (ret < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Could not set input format %s.\n", video_data_conf->v4l2f));
return -1;
}
}
#endif
if (strcmp(video_data_conf->format, "") != 0) {
in_fmt = av_find_input_format(video_data_conf->format);
if (in_fmt == NULL) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot find the format %s.\n", video_data_conf->format));
return -1;
}
}
video_input_file->av_fmt_ctx = NULL;
if (video_data_conf->demux_buffer_size) {
char szBufSize[100];
sprintf(szBufSize, "%d", video_data_conf->demux_buffer_size);
ret = av_dict_set(&options, "buffer_size", szBufSize, 0);
if (ret < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Could not set demuxer's input buffer size.\n"));
return -1;
}
}
open_res = avformat_open_input(&video_input_file->av_fmt_ctx, video_data_conf->filename, in_fmt, options ? &options : NULL);
if ( (open_res < 0) && !stricmp(video_data_conf->filename, "screen-capture-recorder") ) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Buggy screen capture input (open failed with code %d), retrying without specifying resolution\n", open_res));
av_dict_set(&options, "video_size", NULL, 0);
open_res = avformat_open_input(&video_input_file->av_fmt_ctx, video_data_conf->filename, in_fmt, options ? &options : NULL);
}
if ( (open_res < 0) && options) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Error %d opening input - retrying without options\n", open_res));
av_dict_free(&options);
open_res = avformat_open_input(&video_input_file->av_fmt_ctx, video_data_conf->filename, in_fmt, NULL);
}
if (open_res < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open file %s\n", video_data_conf->filename));
return -1;
}
if (avformat_find_stream_info(video_input_file->av_fmt_ctx, NULL) < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot find stream information\n"));
return -1;
}
av_dump_format(video_input_file->av_fmt_ctx, 0, video_data_conf->filename, 0);
video_input_file->vstream_idx = -1;
for (i = 0; i < video_input_file->av_fmt_ctx->nb_streams; i++) {
if (video_input_file->av_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
video_input_file->vstream_idx = i;
break;
}
}
if (video_input_file->vstream_idx == -1) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot find a video stream\n"));
return -1;
}
codec_ctx = video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->codec;
codec = avcodec_find_decoder(codec_ctx->codec_id);
if (codec == NULL) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Codec is not supported.\n"));
if (!video_input_file->av_fmt_ctx_ref_cnt)
avformat_close_input(&video_input_file->av_fmt_ctx);
return -1;
}
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open codec.\n"));
if (!video_input_file->av_fmt_ctx_ref_cnt)
avformat_close_input(&video_input_file->av_fmt_ctx);
return -1;
}
video_input_file->width = codec_ctx->width;
video_input_file->height = codec_ctx->height;
video_input_file->sar = codec_ctx->sample_aspect_ratio;
video_input_file->pix_fmt = codec_ctx->pix_fmt;
if (codec_ctx->time_base.num==1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("AVCTX give frame duration of %d/%d - keeping requested rate %d, but this may result in unexpected behaviour.\n", codec_ctx->time_base.num, codec_ctx->time_base.den, video_data_conf->framerate ));
if (codec_ctx->time_base.den==1000000) {
codec_ctx->time_base.num = codec_ctx->time_base.den / video_data_conf->framerate;
}
}
else if (video_data_conf->framerate >= 0 && codec_ctx->time_base.num) {
video_data_conf->framerate = codec_ctx->time_base.den / codec_ctx->time_base.num;
}
if (video_data_conf->framerate <= 1 || video_data_conf->framerate > 1000) {
const int num = video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->avg_frame_rate.num;
const int den = video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->avg_frame_rate.den == 0 ? 1 : video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->avg_frame_rate.den;
video_data_conf->framerate = num / den;
if (video_data_conf->framerate / 1000 != 0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Framerate %d was divided by 1000: %d\n", video_data_conf->framerate, video_data_conf->framerate/1000));
video_data_conf->framerate = video_data_conf->framerate / 1000;
}
if (video_data_conf->framerate <= 1 || video_data_conf->framerate > 1000) {
video_data_conf->framerate = num / den;
if (video_data_conf->framerate / 1000 != 0) {
video_data_conf->framerate = video_data_conf->framerate / 1000;
}
}
}
if (video_data_conf->framerate <= 1 || video_data_conf->framerate > 1000) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Invalid input framerate %d (AVCTX timebase is %d/%d).\n", video_data_conf->framerate, codec_ctx->time_base.num, codec_ctx->time_base.den));
return -1;
}
video_data_conf->time_base = video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->time_base;
video_input_file->mode = mode;
video_input_file->no_loop = no_loop;
video_input_file->nb_consumers = nb_consumers;
return 0;
}
int dc_video_decoder_read(VideoInputFile *video_input_file, VideoInputData *video_input_data, int source_number, int use_source_timing, int is_live_capture, const int *exit_signal_addr)
{
#ifdef DASHCAST_DEBUG_TIME_
struct timeval start, end;
long elapsed_time;
#endif
AVPacket packet;
int ret, got_frame, already_locked = 0;
AVCodecContext *codec_ctx;
VideoDataNode *video_data_node;
codec_ctx = video_input_file->av_fmt_ctx->streams[video_input_file->vstream_idx]->codec;
while (1) {
#ifdef DASHCAST_DEBUG_TIME_
gf_gettimeofday(&start, NULL);
#endif
memset(&packet, 0, sizeof(AVPacket));
ret = av_read_frame(video_input_file->av_fmt_ctx, &packet);
#ifdef DASHCAST_DEBUG_TIME_
gf_gettimeofday(&end, NULL);
elapsed_time = (end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec);
fprintf(stdout, "fps: %f\n", 1000000.0/elapsed_time);
#endif
if (video_input_file->av_fmt_ctx_ref_cnt && ((packet.stream_index != video_input_file->vstream_idx) || (ret == AVERROR_EOF))) {
AVPacket *packet_copy = NULL;
if (ret != AVERROR_EOF) {
GF_SAFEALLOC(packet_copy, AVPacket);
if (packet_copy)
memcpy(packet_copy, &packet, sizeof(AVPacket));
}
assert(video_input_file->av_pkt_list);
gf_mx_p(video_input_file->av_pkt_list_mutex);
gf_list_add(video_input_file->av_pkt_list, packet_copy);
gf_mx_v(video_input_file->av_pkt_list_mutex);
if (ret != AVERROR_EOF) {
continue;
}
}
if (ret == AVERROR_EOF) {
if (video_input_file->mode == LIVE_MEDIA && video_input_file->no_loop == 0) {
av_seek_frame(video_input_file->av_fmt_ctx, video_input_file->vstream_idx, 0, 0);
av_free_packet(&packet);
continue;
}
dc_producer_lock(&video_input_data->producer, &video_input_data->circular_buf);
dc_producer_unlock_previous(&video_input_data->producer, &video_input_data->circular_buf);
video_data_node = (VideoDataNode *) dc_producer_produce(&video_input_data->producer, &video_input_data->circular_buf);
video_data_node->source_number = source_number;
memset(&packet, 0, sizeof(AVPacket));
#ifndef FF_API_AVFRAME_LAVC
avcodec_get_frame_defaults(video_data_node->vframe);
#else
av_frame_unref(video_data_node->vframe);
#endif
avcodec_decode_video2(codec_ctx, video_data_node->vframe, &got_frame, &packet);
if (got_frame) {
dc_producer_advance(&video_input_data->producer, &video_input_data->circular_buf);
return 0;
}
dc_producer_end_signal(&video_input_data->producer, &video_input_data->circular_buf);
dc_producer_unlock(&video_input_data->producer, &video_input_data->circular_buf);
return -2;
}
else if (ret < 0)
{
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot read video frame.\n"));
continue;
}
if (packet.stream_index == video_input_file->vstream_idx) {
u32 nb_retry = 10;
while (!already_locked) {
if (dc_producer_lock(&video_input_data->producer, &video_input_data->circular_buf) < 0) {
if (!nb_retry) break;
gf_sleep(10);
nb_retry--;
continue;
}
dc_producer_unlock_previous(&video_input_data->producer, &video_input_data->circular_buf);
already_locked = 1;
}
if (!already_locked) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[dashcast] Live system dropped a video frame\n"));
continue;
}
video_data_node = (VideoDataNode *) dc_producer_produce(&video_input_data->producer, &video_input_data->circular_buf);
video_data_node->source_number = source_number;
#ifndef FF_API_AVFRAME_LAVC
avcodec_get_frame_defaults(video_data_node->vframe);
#else
av_frame_unref(video_data_node->vframe);
#endif
video_data_node->frame_ntp = gf_net_get_ntp_ts();
video_data_node->frame_utc = gf_net_get_utc();
if (avcodec_decode_video2(codec_ctx, video_data_node->vframe, &got_frame, &packet) < 0) {
av_free_packet(&packet);
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Error while decoding video.\n"));
dc_producer_end_signal(&video_input_data->producer, &video_input_data->circular_buf);
dc_producer_unlock(&video_input_data->producer, &video_input_data->circular_buf);
return -1;
}
if (got_frame) {
if (use_source_timing && is_live_capture) {
u64 pts;
if (video_input_file->pts_init == 0) {
video_input_file->pts_init = 1;
video_input_file->utc_at_init = gf_net_get_utc();
video_input_file->first_pts = packet.pts;
video_input_file->prev_pts = 0;
video_input_data->frame_duration = 0;
}
#if 0
if (video_input_file->pts_init && (video_input_file->pts_init!=3) ) {
if (packet.pts==AV_NOPTS_VALUE) {
video_input_file->pts_init=1;
} else if (video_input_file->pts_init==1) {
video_input_file->pts_init=2;
video_input_file->pts_dur_estimate = packet.pts;
} else if (video_input_file->pts_init==2) {
video_input_file->pts_init=3;
video_input_data->frame_duration = packet.pts - video_input_file->pts_dur_estimate;
video_input_file->sync_tolerance = 9*video_input_data->frame_duration/5;
}
}
#endif
if (packet.pts!=AV_NOPTS_VALUE) {
pts = packet.pts - video_input_file->first_pts;
} else {
pts = video_input_file->prev_pts + video_input_data->frame_duration;
}
#ifndef GPAC_DISABLE_LOG
if (0 && gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_WARNING)) {
if (pts - video_input_file->prev_pts > video_input_file->sync_tolerance) {
u32 nb_lost=0;
while (video_input_file->prev_pts + video_input_data->frame_duration + video_input_file->sync_tolerance < pts) {
video_input_file->prev_pts += video_input_data->frame_duration;
nb_lost++;
}
if (nb_lost) {
GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DashCast] Capture lost %d video frames \n", nb_lost));
}
}
}
#endif
#if 1
if ((pts != video_input_file->prev_pts) && (video_input_file->pts_init == 1)) {
video_input_file->pts_init = 2;
video_input_data->frame_duration = pts - video_input_file->prev_pts;
video_input_file->sync_tolerance = 9*video_input_data->frame_duration/5;
}
#endif
video_input_file->prev_pts = pts;
video_data_node->vframe->pts = pts;
}
if (video_data_node->vframe->pts==AV_NOPTS_VALUE) {
if (!use_source_timing) {
video_data_node->vframe->pts = video_input_file->frame_decoded;
} else {
video_data_node->vframe->pts = video_data_node->vframe->pkt_pts;
}
}
video_input_file->frame_decoded++;
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Video Frame TS "LLU" decoded at UTC "LLU" ms (frame duration %d)\n", video_data_node->vframe->pts, gf_net_get_utc(), video_input_data->frame_duration));
if (codec_ctx->codec->id == CODEC_ID_RAWVIDEO) {
if (is_live_capture && !video_input_data->frame_duration) {
} else {
video_data_node->nb_raw_frames_ref = video_input_file->nb_consumers;
video_data_node->raw_packet = packet;
dc_producer_advance(&video_input_data->producer, &video_input_data->circular_buf);
while (video_data_node->nb_raw_frames_ref && ! *exit_signal_addr) {
gf_sleep(0);
}
}
} else {
dc_producer_advance(&video_input_data->producer, &video_input_data->circular_buf);
av_free_packet(&packet);
}
return 0;
}
}
av_free_packet(&packet);
}
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Unknown error while reading video frame.\n"));
return -1;
}
void dc_video_decoder_close(VideoInputFile *video_input_file)
{
if (!video_input_file->av_fmt_ctx_ref_cnt)
avformat_close_input(&video_input_file->av_fmt_ctx);
video_input_file->av_pkt_list = NULL;
video_input_file->av_pkt_list_mutex = NULL;
}