/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "sabbu.h" #ifdef DISABLE_FFMPEG gboolean video_init() { return FALSE; } int video_get_height(struct video_ffmpeg *ffmpeg) { return -1; } int video_get_width(struct video_ffmpeg *ffmpeg) { return -1; } double video_get_fps(struct video_ffmpeg *video) { return 0; } int64_t video_get_duration(struct video_ffmpeg *ffmpeg) { return -1; } struct video_ffmpeg *video_open(char *file) { return NULL; } void video_close(struct video_ffmpeg *video) { } int video_get_frame(struct video_ffmpeg *video, AVPacket *packet) { } int video_get_picture(struct video_ffmpeg *video, AVPicture *picture) { } gboolean video_seek(struct video_ffmpeg *ffmpeg, long pos) { } #else extern "C" { #ifndef _WINDOWS #include #include #else void (*av_register_all)(void) = NULL; int (*av_open_input_file)(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap) = NULL; int (*av_find_stream_info)(AVFormatContext *ic) = NULL; void (*av_close_input_file)(AVFormatContext *s) = NULL; AVCodec *(*avcodec_find_decoder)(enum CodecID id) = NULL; int (*avcodec_open)(AVCodecContext *avctx, AVCodec *codec) = NULL; int (*avcodec_close)(AVCodecContext *avctx) = NULL; int (*av_read_frame)(AVFormatContext *s, AVPacket *pkt) = NULL; int (*av_seek_frame)(AVFormatContext *s, int stream_index, int64_t timestamp) = NULL; int (*img_convert)(AVPicture *dst, int dst_pix_fmt, const AVPicture *src, int pix_fmt, int width, int height) = NULL; AVFrame *(*avcodec_alloc_frame)(void) = NULL; int (*avcodec_decode_video)(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, uint8_t *buf, int buf_size) = NULL; void (*av_free)(void *ptr) = NULL; void (*avcodec_flush_buffers)(AVCodecContext *avctx) = NULL; int (*avpicture_alloc)(AVPicture *picture, int pix_fmt, int width, int height) = NULL; void (*avpicture_free)(AVPicture *picture) = NULL; #endif } GModule *module_avformat = NULL; GModule *module_avcodec = NULL; extern struct sabbu app; int load_symbol(GModule *module, char *text, void *dst) { if(!g_module_symbol(module, text, (gpointer*) dst)) { video_uninit(); throw new kryError(0, _("Failed to load symbol '%s' from video library. Make sure sabbu is installed properly.\nVideo functionality will be disabled."), text); return 0; } return 1; } gboolean video_init() { #ifdef _WINDOWS module_avformat = g_module_open("avformat.dll", G_MODULE_BIND_LAZY); if(!module_avformat) module_avformat = g_module_open("c:\\sabbu\\avformat.dll", G_MODULE_BIND_LAZY); if(!module_avformat) { throw kryError(_("Unable to load the 'avformat' library. Make sure sabbu is installed properly.\nVideo functionality will be disabled.")); return FALSE; } if(!load_symbol(module_avformat, "av_register_all", &av_register_all)) return FALSE; else if(!load_symbol(module_avformat, "av_open_input_file", &av_open_input_file)) return FALSE; else if(!load_symbol(module_avformat, "av_find_stream_info", &av_find_stream_info)) return FALSE; else if(!load_symbol(module_avformat, "av_close_input_file", &av_close_input_file)) return FALSE; else if(!load_symbol(module_avformat, "av_read_frame", &av_read_frame)) return FALSE; else if(!load_symbol(module_avformat, "av_close_input_file", &av_close_input_file)) return FALSE; else if(!load_symbol(module_avformat, "av_seek_frame", &av_seek_frame)) return FALSE; module_avcodec = g_module_open("avcodec.dll", G_MODULE_BIND_LAZY); if(!module_avcodec) module_avcodec = g_module_open("c:\\sabbu\\avcodec.dll", G_MODULE_BIND_LAZY); if(!module_avcodec) { video_uninit(); throw kryError(_("Unable to load the 'avcodec' library. Make sure sabbu is installed properly.\nVideo functionality will be disabled.")); return FALSE; } if(!load_symbol(module_avcodec, "avcodec_find_decoder", &avcodec_find_decoder)) return FALSE; else if(!load_symbol(module_avcodec, "avcodec_open", &avcodec_open)) return FALSE; else if(!load_symbol(module_avcodec, "avcodec_close", &avcodec_close)) return FALSE; else if(!load_symbol(module_avcodec, "img_convert", &img_convert)) return FALSE; else if(!load_symbol(module_avcodec, "avcodec_alloc_frame", &avcodec_alloc_frame)) return FALSE; else if(!load_symbol(module_avcodec, "avcodec_decode_video", &avcodec_decode_video)) return FALSE; else if(!load_symbol(module_avcodec, "avcodec_flush_buffers", &avcodec_flush_buffers)) return FALSE; else if(!load_symbol(module_avcodec, "av_free", &av_free)) return FALSE; else if(!load_symbol(module_avcodec, "avpicture_alloc", &avpicture_alloc)) return FALSE; else if(!load_symbol(module_avcodec, "avpicture_free", &avpicture_free)) return FALSE; #endif av_register_all(); return TRUE; } struct video_ffmpeg *video_open(char *filename) { struct video_ffmpeg *video = (struct video_ffmpeg *) malloc(sizeof(struct video_ffmpeg)); AVCodecContext *enc; int err, i; AVCodec *codec; video->video_index = -1; video->last_pkt = NULL; char *file_locale = g_locale_from_utf8(filename, -1, NULL, NULL, NULL); err = av_open_input_file(&video->ic, file_locale, NULL, 0, NULL); if (err < 0) { g_free(file_locale); throw kryError(_("The video file '%s' could not be opened"), filename); return 0; } g_free(file_locale); err = av_find_stream_info(video->ic); if (err < 0) { av_close_input_file(video->ic); throw kryError(_("Could not find stream information inside the video file. File may be corrupted.")); } for(i = 0; i < video->ic->nb_streams; i++) { AVCodecContext *enc = video->ic->streams[i]->codec; if(enc->codec_type == CODEC_TYPE_VIDEO) { video->video_index = i; break; } } if(video->video_index == -1) { av_close_input_file(video->ic); throw kryError(_("The video file does not seem to contain a video stream.")); } enc = video->ic->streams[video->video_index]->codec; enc->debug_mv = 0; enc->debug = 0; enc->workaround_bugs = 1; enc->thread_count= 1; codec = avcodec_find_decoder(enc->codec_id); if (!codec || avcodec_open(enc, codec) < 0) throw kryError(_("The codec used in the video file cannot be opened in Sabbu")); video->filename = kry_strdup(filename); return video; } int video_get_frame(struct video_ffmpeg *video, AVPacket *pkt) { int ret; AVPacket *pkt_ptr = (AVPacket *) malloc(sizeof(AVPacket)); if(video->last_pkt) { av_free_packet(video->last_pkt); free(video->last_pkt); video->last_pkt = NULL; } for(;;) { ret = av_read_frame(video->ic, pkt_ptr); if (ret < 0) { return 0; } if(pkt_ptr->stream_index == video->video_index) { video->last_pkt = pkt_ptr; *pkt = *pkt_ptr; return 1; } av_free_packet(pkt_ptr); } } void video_free_picture(struct video_ffmpeg *vide, AVPicture *pict) { avpicture_free(pict); } int video_get_picture(struct video_ffmpeg *video, AVPicture *pict) { AVFrame *frame; int dst_pix_fmt = PIX_FMT_RGBA32; int got_picture; int len; int width, height; AVPacket pkt; if(!video->last_pkt) return FALSE; frame = avcodec_alloc_frame(); while(1) { len = avcodec_decode_video(video->ic->streams[video->video_index]->codec, frame, &got_picture, video->last_pkt->data, video->last_pkt->size); if(got_picture) break; if(!video_get_frame(video, &pkt)) return FALSE; } width = video->ic->streams[video->video_index]->codec->width; height = video->ic->streams[video->video_index]->codec->height; pict->data[0] = (uint8_t*) malloc(4 * width * height); pict->linesize[0] = width * 4; if(img_convert(pict, dst_pix_fmt, (AVPicture *) frame, video->ic->streams[video->video_index]->codec->pix_fmt, width, height) < 0) { av_free(frame); return FALSE; } av_free(frame); return TRUE; } int video_get_width(struct video_ffmpeg *video) { return video->ic->streams[video->video_index]->codec->width; } int video_get_height(struct video_ffmpeg *video) { return video->ic->streams[video->video_index]->codec->height; } double video_get_fps(struct video_ffmpeg *video) { return (video->ic->streams[video->video_index]->r_frame_rate.num / (double) video->ic->streams[video->video_index]->r_frame_rate.den); } int64_t video_get_duration(struct video_ffmpeg *video) { return video->ic->duration; } void video_close(struct video_ffmpeg *video) { if(video->last_pkt) { free(video->last_pkt); video->last_pkt = NULL; } avcodec_close(video->ic->streams[video->video_index]->codec); av_close_input_file(video->ic); } gboolean video_seek(struct video_ffmpeg *video, long time) { AVPacket pkt; AVPicture pict; gboolean rv = TRUE; int64_t target_time = (int64_t) ((time / 1000.0) * AV_TIME_BASE); GTimer *timer; int64_t delta = 0; int delta_mili; if(video->last_pkt) { delta = target_time - video->last_pkt->pts; delta_mili = (int) (((double) delta / AV_TIME_BASE) * 1000.0); } if(!video->last_pkt || delta < 0 || delta > 1000) { #ifdef FFMPEG_SEEK_PARAM4 if(av_seek_frame(video->ic, -1, target_time, 0) < 0) #else if(av_seek_frame(video->ic, -1, target_time) < 0) #endif { g_warning("seek failed... %lld", (long long) target_time); return FALSE; } avcodec_flush_buffers(video->ic->streams[video->video_index]->codec); } if(video->last_pkt) { free(video->last_pkt); video->last_pkt = NULL; } timer = g_timer_new(); g_timer_start(timer); gboolean next_frame = FALSE; while(1) { if(!(rv = video_get_frame(video, &pkt))) break; if(next_frame) { int mili = (int) ((pkt.pts / AV_TIME_BASE) * 1000); char *str = time_mili_to_string(mili); kry_free(str); break; } if(pkt.pts >= target_time) next_frame = TRUE; video_get_picture(video, &pict); free(pict.data[0]); if(g_timer_elapsed(timer, NULL) > 0.1) { gtk_progress_bar_pulse(app.ui.tab_video.progress_bar); while(gtk_events_pending()) gtk_main_iteration(); g_timer_start(timer); } } gtk_progress_bar_set_text(app.ui.tab_video.progress_bar, __("VideoStatus|Ready")); gtk_progress_bar_set_fraction(app.ui.tab_video.progress_bar, 0); return rv; } void video_uninit() { if(module_avformat) { g_module_close(module_avformat); module_avformat = NULL; } if(module_avcodec) { g_module_close(module_avcodec); module_avcodec = NULL; } } #endif