diff --git a/Makefile.in b/Makefile.in index 1d40a3877..35904c6b7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -447,6 +447,7 @@ alsa: @ALSA_PLAY_LIB_TARGET@ @ALSA_CAP_LIB_TARGET@ portaudio: @PORTAUDIO_PLAY_LIB_TARGET@ @PORTAUDIO_CAP_LIB_TARGET@ jack: @JACK_CAP_LIB_TARGET@ @JACK_PLAY_LIB_TARGET@ scale: @SCALE_LIB_TARGET@ +libavcodec: @LIBAVCODEC_DECOMPRESS_LIB_TARGET@ @LIBAVCODEC_COMPRESS_LIB_TARGET@ @SDL_LIB_TARGET@: @SDL_OBJ@ mkdir -p lib/ultragrid @@ -512,6 +513,14 @@ scale: @SCALE_LIB_TARGET@ mkdir -p lib/ultragrid $(LINKER) $(LDFLAGS) -shared -Wl,-soname,vcompress_uyvy.so.@video_compress_abi_version@ @GL_COMMON_OBJ@ @UYVY_COMPRESS_OBJ@ -o $@ +@LIBAVCODEC_COMPRESS_LIB_TARGET@: @LIBAVCODEC_COMPRESS_OBJ@ + mkdir -p lib/ultragrid + $(LINKER) $(LDFLAGS) -shared -Wl,-soname,vcompress_libavcodec.so.@video_compress_abi_version@ $^ @LIBAVCODEC_LIBS@ -o $@ + +@LIBAVCODEC_DECOMPRESS_LIB_TARGET@: @LIBAVCODEC_DECOMPRESS_OBJ@ + mkdir -p lib/ultragrid + $(LINKER) $(LDFLAGS) -shared -Wl,-soname,vdecompress_libavcodec.so.@video_decompress_abi_version@ $^ @LIBAVCODEC_LIBS@ -o $@ + @JPEG_COMPRESS_LIB_TARGET@: @JPEG_COMPRESS_OBJ@ @JPEG_COMMON_OBJ@ mkdir -p lib/ultragrid $(LINKER) $(LDFLAGS) -shared -Wl,-soname,vcompress_jpeg.so.@video_compress_abi_version@ $^ @JPEG_LIB@ -o $@ diff --git a/configure.ac b/configure.ac index f396ccfe8..ca92287a2 100644 --- a/configure.ac +++ b/configure.ac @@ -2027,8 +2027,60 @@ AC_SUBST(V4L2_OBJ) LIB_MODULES="$LIB_MODULES $V4L2_LIBS" +# ------------------------------------------------------------------------------------------------- +# Libav +# ------------------------------------------------------------------------------------------------- +libavcodec=no +AC_ARG_ENABLE(libavcodec, +AS_HELP_STRING([--disable-libavcodec], [disable libavcodec support(default is auto)]), +[libavcodec_req=$enableval], +[libavcodec_req=yes] +) + +#PKG_CHECK_MODULES([LIBAVCODEC], [libavcodec], [found_libavcodec=yes], [found_libavcodec=no]) +AC_CHECK_HEADERS([libavcodec/avcodec.h libavutil/imgutils.h libavutil/opt.h]) +AC_CHECK_LIB(avcodec, avcodec_open2) +if test $ac_cv_header_libavcodec_avcodec_h = yes -a \ + $ac_cv_header_libavutil_imgutils_h = yes -a \ + $ac_cv_header_libavutil_opt_h = yes -a \ + $ac_cv_lib_avcodec_avcodec_open2 = yes +then + found_libavcodec=yes + LIBAVCODEC_LIBS="-lavcodec" +else + found_libavcodec=no +fi + +if test $libavcodec_req = yes -a $found_libavcodec = yes +then + LIBAVCODEC_OBJ="src/video_compress/libavcodec.o" + AC_DEFINE([HAVE_LIBAVCODEC], [1], [Build with LIBAVCODEC support]) + AC_SUBST(LIBAVCODEC_COMPRESS_LIB_TARGET, "lib/ultragrid/vcompress_libavcodec.so.$video_compress_abi_version") + AC_SUBST(LIBAVCODEC_DECOMPRESS_LIB_TARGET, "lib/ultragrid/vdecompress_libavcodec.so.$video_decompress_abi_version") + LIB_TARGETS="$LIB_TARGETS $LIBAVCODEC_COMPRESS_LIB_TARGET $LIBAVCODEC_DECOMPRESS_LIB_TARGET" + LIBAVCODEC_COMPRESS_OBJ=src/video_compress/libavcodec.o + LIBAVCODEC_DECOMPRESS_OBJ=src/video_decompress/libavcodec.o + LIB_OBJS="$LIB_OBJS $LIBAVCODEC_COMPRESS_OBJ $LIBAVCODEC_DECOMPRESS_OBJ" + CFLAGS="$CFLAGS $LIBAVCODEC_CFLAGS" + libavcodec=yes + SAVED_LIBS=$LIBS + LIBS="$LIBS $LIBAVCODEC_LIBS" + AC_CHECK_FUNCS(avcodec_encode_video2) + LIBS=$SAVED_LIBS +else + LIBAVCODEC_LIBS= + LIBAVCODEC_OBJ= +fi + +AC_SUBST(LIBAVCODEC_LIBS) # set by pkg-config +AC_SUBST(LIBAVCODEC_COMPRESS_OBJ) +AC_SUBST(LIBAVCODEC_DECOMPRESS_OBJ) + +LIB_MODULES="$LIB_MODULES $LIBAVCODEC_LIBS" + # ------------------------------------------------------------------------------------------------- # We need to add libraries then +# ------------------------------------------------------------------------------------------------- # this is only needed when passing to "clean" make target if test "$build_libraries" = yes @@ -2062,6 +2114,7 @@ Configuring JPEG /bin/sh -c "cd gpujpeg; ./configure --with-cuda=$CUDA_PATH" fi +LIB_MODULES="$LIB_MODULES $V4L2_LIBS" # ------------------------------------------------------------------------------------------------- # Finally, substitute things into the Makefile and config.h @@ -2095,6 +2148,7 @@ RESULT=\ Realtime DXT (OpenGL) ....... $rtdxt JPEG ........................ $jpeg (static: $jpeg_static) UYVY dummy compression ...... $uyvy + Libavcodec .................. $libavcodec scale postprocessor ......... $scale testcard extras ............. $testcard_extras_req diff --git a/src/video_compress.c b/src/video_compress.c index 3c1ae5eb2..e922908d2 100644 --- a/src/video_compress.c +++ b/src/video_compress.c @@ -57,6 +57,7 @@ #include "video_compress.h" #include "video_compress/dxt_glsl.h" #include "video_compress/fastdxt.h" +#include "video_compress/libavcodec.h" #include "video_compress/jpeg.h" #include "video_compress/none.h" #include "video_compress/uyvy.h" @@ -99,6 +100,9 @@ struct compress_t compress_modules[] = { #endif #if defined HAVE_COMPRESS_UYVY || defined BUILD_LIBRARIES {"UYVY", "uyvy", MK_NAME(uyvy_compress_init), MK_NAME(uyvy_compress), MK_NAME(uyvy_compress_done), NULL}, +#endif +#if defined HAVE_LIBAVCODEC || defined BUILD_LIBRARIES + {"libavcodec", "libavcodec", MK_NAME(libavcodec_compress_init), MK_NAME(libavcodec_compress), MK_NAME(libavcodec_compress_done), NULL}, #endif {"none", NULL, MK_STATIC(none_compress_init), MK_STATIC(none_compress), MK_STATIC(none_compress_done), NULL}, }; diff --git a/src/video_compress/libavcodec.c b/src/video_compress/libavcodec.c new file mode 100644 index 000000000..9b0cd4e4f --- /dev/null +++ b/src/video_compress/libavcodec.c @@ -0,0 +1,360 @@ +/* + * FILE: video_compress/libavcodec.c + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * Copyright (c) 2005-2011 CESNET z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by CESNET z.s.p.o. + * + * 4. Neither the name of the CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif // HAVE_CONFIG_H + +#include "video_compress/libavcodec.h" + +#include +#include +#include +#include + +#include "debug.h" +#include "host.h" +#include "video.h" +#include "video_codec.h" + +#define DEFAULT_CODEC CODEC_ID_H264 + +struct libav_video_compress { + struct video_frame *out[2]; + struct video_desc saved_desc; + + AVFrame *in_frame; + AVCodec *codec; + AVCodecContext *codec_ctx; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + AVPacket pkt[2]; +#endif + + unsigned char *decoded; + decoder_t decoder; + + bool configured; +}; + +static void to_yuv420(AVFrame *out_frame, unsigned char *in_data); + +void * libavcodec_compress_init(char * fmt) +{ + UNUSED(fmt); + struct libav_video_compress *s; + + s = (struct libav_video_compress *) malloc(sizeof(struct libav_video_compress)); + s->out[0] = s->out[1] = NULL; + s->configured = false; + + s->codec = NULL; + s->codec_ctx = NULL; + s->in_frame = NULL; + + s->decoded = NULL; + +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + for(int i = 0; i < 2; ++i) { + av_init_packet(&s->pkt[i]); + s->pkt[i].data = NULL; + s->pkt[i].size = 0; + } +#endif + + /* register all the codecs (you can also register only the codec + * you wish to have smaller code */ + avcodec_register_all(); + + return s; +} + +static bool configure_with(struct libav_video_compress *s, struct video_frame *frame) +{ + int ret; + int codec_id = DEFAULT_CODEC; + // implement multiple tiles support if needed + assert(frame->tile_count == 1); + s->saved_desc = video_desc_from_frame(frame); + + struct video_desc compressed_desc; + compressed_desc = video_desc_from_frame(frame); + switch(codec_id) { + case CODEC_ID_H264: + compressed_desc.color_spec = H264; + break; + default: + fprintf(stderr, "[Libavcodec] Unable to match " + "desired codec to UltraGrid internal " + "one.\n"); + return false; + + } + + for(int i = 0; i < 2; ++i) { + s->out[i] = vf_alloc_desc(compressed_desc); +#ifndef HAVE_AVCODEC_ENCODE_VIDEO2 + s->out[i]->tiles[0].data = malloc(compressed_desc.width * + compressed_desc.height * 4); +#endif // HAVE_AVCODEC_ENCODE_VIDEO2 + } + + /* find the video encoder */ + s->codec = avcodec_find_encoder(codec_id); + if (!s->codec) { + fprintf(stderr, "Libavcodec doesn't contain specified codec (H.264).\n" + "Hint: Check if you have libavcodec-extra package installed.\n"); + return false; + } + + // avcodec_alloc_context3 allocates context and sets default value + s->codec_ctx = avcodec_alloc_context3(s->codec); + if (!s->codec_ctx) { + fprintf(stderr, "Could not allocate video codec context\n"); + return false; + } + + /* put parameters */ + s->codec_ctx->bit_rate = frame->tiles[0].width * frame->tiles[0].height * + 4 * /* for H.264: 1 - low motion, 2 - medium motion, 4 - high motion */ + 0.07 * frame->fps; + /* resolution must be a multiple of two */ + s->codec_ctx->width = frame->tiles[0].width; + s->codec_ctx->height = frame->tiles[0].height; + /* frames per second */ + s->codec_ctx->time_base= (AVRational){1,(int) frame->fps}; + s->codec_ctx->gop_size = 20; /* emit one intra frame every ten frames */ + s->codec_ctx->max_b_frames = 0; + switch(frame->color_spec) { + case Vuy2: + case DVS8: + case UYVY: + s->decoder = (decoder_t) memcpy; + break; + case YUYV: + s->decoder = (decoder_t) vc_copylineYUYV; + break; + case v210: + s->decoder = (decoder_t) vc_copylinev210; + break; + default: + fprintf(stderr, "[Libavcodec] Unable to find " + "appropriate pixel format.\n"); + return false; + } +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + s->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; +#else + s->codec_ctx->pix_fmt = PIX_FMT_YUV420P; +#endif + + s->decoded = malloc(frame->tiles[0].width * frame->tiles[0].height * 4); + + if(codec_id == CODEC_ID_H264) { + av_opt_set(s->codec_ctx->priv_data, "preset", "ultrafast", 0); + //av_opt_set(s->codec_ctx->priv_data, "tune", "fastdecode", 0); + av_opt_set(s->codec_ctx->priv_data, "tune", "zerolatency", 0); + } + + /* open it */ + if (avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + return false; + } + + s->in_frame = avcodec_alloc_frame(); + if (!s->in_frame) { + fprintf(stderr, "Could not allocate video frame\n"); + return false; + } + s->in_frame->format = s->codec_ctx->pix_fmt; + s->in_frame->width = s->codec_ctx->width; + s->in_frame->height = s->codec_ctx->height; + + /* the image can be allocated by any means and av_image_alloc() is + * just the most convenient way if av_malloc() is to be used */ + ret = av_image_alloc(s->in_frame->data, s->in_frame->linesize, + s->codec_ctx->width, s->codec_ctx->height, + s->codec_ctx->pix_fmt, 32); + if (ret < 0) { + fprintf(stderr, "Could not allocate raw picture buffer\n"); + return false; + } + + return true; +} + +static void to_yuv420(AVFrame *out_frame, unsigned char *in_data) +{ + for(int y = 0; y < (int) out_frame->height; ++y) { + unsigned char *src = in_data + out_frame->width * y * 2; + unsigned char *dst_y = out_frame->data[0] + out_frame->linesize[0] * y; + for(int x = 0; x < out_frame->width; ++x) { + dst_y[x] = src[x * 2 + 1]; + } + } + + for(int y = 0; y < (int) out_frame->height / 2; ++y) { + /* every even row */ + unsigned char *src1 = in_data + (y * 2) * (out_frame->width * 2); + /* every odd row */ + unsigned char *src2 = in_data + (y * 2 + 1) * (out_frame->width * 2); + unsigned char *dst_cb = out_frame->data[1] + out_frame->linesize[1] * y; + unsigned char *dst_cr = out_frame->data[2] + out_frame->linesize[2] * y; + for(int x = 0; x < out_frame->width / 2; ++x) { + dst_cb[x] = (src1[x * 4] + src2[x * 4]) / 2; + dst_cr[x] = (src1[x * 4 + 2] + src1[x * 4 + 2]) / 2; + } + } +} + +struct video_frame * libavcodec_compress(void *arg, struct video_frame * tx, int buffer_idx) +{ + struct libav_video_compress *s = (struct libav_video_compress *) arg; + assert (buffer_idx == 0 || buffer_idx == 1); + static int frame_seq = 0; + int ret; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + int got_output; +#endif + + if(!s->configured) { + int ret = configure_with(s, tx); + if(!ret) { + return NULL; + } + s->configured = true; + } else { + // reconfiguration not yet implemented + assert(video_desc_eq(video_desc_from_frame(tx), + s->saved_desc)); + } + + s->in_frame->pts = frame_seq++; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + av_free_packet(&s->pkt[buffer_idx]); + av_init_packet(&s->pkt[buffer_idx]); + s->pkt[buffer_idx].data = NULL; + s->pkt[buffer_idx].size = 0; +#endif + + unsigned char *line1 = (unsigned char *) tx->tiles[0].data; + unsigned char *line2 = (unsigned char *) s->decoded; + int src_linesize = vc_get_linesize(tx->tiles[0].width, tx->color_spec); + int dst_linesize = tx->tiles[0].width * 2; /* UYVY */ + for (int i = 0; i < (int) tx->tiles[0].height; ++i) { + s->decoder(line2, line1, dst_linesize, + 0, 8, 16); + line1 += src_linesize; + line2 += dst_linesize; + } + + to_yuv420(s->in_frame, s->decoded); + +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + /* encode the image */ + ret = avcodec_encode_video2(s->codec_ctx, &s->pkt[buffer_idx], + s->in_frame, &got_output); + if (ret < 0) { + fprintf(stderr, "Error encoding frame\n"); + return NULL; + } + + if (got_output) { + //printf("Write frame %3d (size=%5d)\n", frame_seq, s->pkt[buffer_idx].size); + s->out[buffer_idx]->tiles[0].data = (char *) s->pkt[buffer_idx].data; + s->out[buffer_idx]->tiles[0].data_len = s->pkt[buffer_idx].size; + } else { + return NULL; + } +#else + /* encode the image */ + ret = avcodec_encode_video(s->codec_ctx, (uint8_t *) s->out[buffer_idx]->tiles[0].data, + s->out[buffer_idx]->tiles[0].width * s->out[buffer_idx]->tiles[0].height * 4, + s->in_frame); + if (ret < 0) { + fprintf(stderr, "Error encoding frame\n"); + return NULL; + } + + if (ret) { + //printf("Write frame %3d (size=%5d)\n", frame_seq, s->pkt[buffer_idx].size); + s->out[buffer_idx]->tiles[0].data_len = ret; + } else { + return NULL; + } +#endif // HAVE_AVCODEC_ENCODE_VIDEO2 + + return s->out[buffer_idx]; +} + +void libavcodec_compress_done(void *arg) +{ + struct libav_video_compress *s = (struct libav_video_compress *) arg; + + for(int i = 0; i < 2; ++i) { +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + vf_free(s->out[i]); + av_free_packet(&s->pkt[i]); +#else + vf_free_data(s->out[i]); +#endif // HAVE_AVCODEC_ENCODE_VIDEO2 + } + + if(s->codec_ctx) + avcodec_close(s->codec_ctx); + if(s->in_frame) { + av_freep(s->in_frame->data); + av_free(s->in_frame); + } + av_free(s->codec_ctx); + free(s->decoded); + + free(s); +} + diff --git a/src/video_compress/libavcodec.h b/src/video_compress/libavcodec.h new file mode 100644 index 000000000..2855ab510 --- /dev/null +++ b/src/video_compress/libavcodec.h @@ -0,0 +1,52 @@ +/* + * FILE: video_compress.h + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * Copyright (c) 2005-2010 CESNET z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by CESNET z.s.p.o. + * + * 4. Neither the name of the CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +struct video_frame; + +void *libavcodec_compress_init(char * opts); +struct video_frame *libavcodec_compress(void *args, struct video_frame * tx, int buffer); +void libavcodec_compress_done(void *args); diff --git a/src/video_decompress.c b/src/video_decompress.c index c6d208b53..93a329291 100644 --- a/src/video_decompress.c +++ b/src/video_decompress.c @@ -57,6 +57,7 @@ #include "video_decompress.h" #include "video_decompress/dxt_glsl.h" #include "video_decompress/jpeg.h" +#include "video_decompress/libavcodec.h" #include "video_decompress/null.h" #include "lib_common.h" @@ -129,6 +130,7 @@ struct decode_from_to decoders_for_codec[] = { { DXT5, UYVY, RTDXT_MAGIC }, { JPEG, RGB, JPEG_MAGIC }, { JPEG, UYVY, JPEG_MAGIC }, + { H264, UYVY, LIBAVCODEC_MAGIC }, { (codec_t) -1, (codec_t) -1, NULL_MAGIC } }; const int decoders_for_codec_count = (sizeof(decoders_for_codec) / sizeof(struct decode_from_to)); @@ -143,6 +145,15 @@ decoder_table_t decoders[] = { { JPEG_MAGIC, "jpeg", MK_NAME(jpeg_decompress_init), MK_NAME(jpeg_decompress_reconfigure), MK_NAME(jpeg_decompress), MK_NAME(jpeg_decompress_get_property), MK_NAME(jpeg_decompress_done), NULL}, +#endif +#if defined HAVE_LIBAVCODEC || defined BUILD_LIBRARIES + { LIBAVCODEC_MAGIC, "libavcodec", + MK_NAME(libavcodec_decompress_init), + MK_NAME(libavcodec_decompress_reconfigure), + MK_NAME(libavcodec_decompress), + MK_NAME(libavcodec_decompress_get_property), + MK_NAME(libavcodec_decompress_done), + NULL}, #endif { NULL_MAGIC, NULL, MK_STATIC(null_decompress_init), MK_STATIC(null_decompress_reconfigure), MK_STATIC(null_decompress), MK_NAME(null_decompress_get_property), diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c new file mode 100644 index 000000000..561f1c7e8 --- /dev/null +++ b/src/video_decompress/libavcodec.c @@ -0,0 +1,277 @@ +/* + * FILE: video_decompress/dxt_glsl.c + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * Copyright (c) 2005-2011 CESNET z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by CESNET z.s.p.o. + * + * 4. Neither the name of the CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#endif // HAVE_CONFIG_H + +#include "video_decompress/libavcodec.h" + +#include +#include + +#include "debug.h" +#include "video_decompress.h" + +struct state_libavcodec_decompress { + AVCodec *codec; + AVCodecContext *codec_ctx; + AVFrame *frame; + AVPacket pkt; + + int pitch; + int rshift, gshift, bshift; + int max_compressed_len; + + int last_frame_seq; +}; + +static void to_yuv422(char *dst_buffer, AVFrame *in_frame); + +static void deconfigure(struct state_libavcodec_decompress *s) +{ + if(s->codec_ctx) + avcodec_close(s->codec_ctx); + av_free(s->codec_ctx); + av_free(s->frame); + av_free_packet(&s->pkt); +} + +static bool configure_with(struct state_libavcodec_decompress *s, + struct video_desc desc) +{ + int codec_id; + switch(desc.color_spec) { + case H264: + codec_id = CODEC_ID_H264; + /* find the video encoder */ + break; + default: + fprintf(stderr, "[Libavcodec] Unsupported codec!!!\n"); + return false; + } + + s->codec = avcodec_find_decoder(codec_id); + if(s->codec == NULL) { + fprintf(stderr, "[Libavcodec] Unable to find codec.\n"); + return false; + } + + s->codec_ctx = avcodec_alloc_context3(s->codec); + if(s->codec_ctx == NULL) { + fprintf(stderr, "[Libavcodec] Unable to allocate codec context.\n"); + return false; + } + + // zero should mean count equal to the number of virtual cores + s->codec_ctx->thread_count = 0; + s->codec_ctx->thread_type = FF_THREAD_SLICE; + + if(avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { + fprintf(stderr, "[Libavcodec] Unable to open decoder.\n"); + return false; + } + + s->frame = avcodec_alloc_frame(); + if(!s->frame) { + fprintf(stderr, "[Libavcodec] Unable allocate frame.\n"); + return false; + } + + av_init_packet(&s->pkt); + + s->last_frame_seq = -1; + + return true; +} + +void * libavcodec_decompress_init(void) +{ + struct state_libavcodec_decompress *s; + + s = (struct state_libavcodec_decompress *) + malloc(sizeof(struct state_libavcodec_decompress)); + + /* register all the codecs (you can also register only the codec + * you wish to have smaller code */ + avcodec_register_all(); + + s->codec_ctx = NULL;; + s->frame = NULL; + av_init_packet(&s->pkt); + s->pkt.data = NULL; + s->pkt.size = 0; + + return s; +} + +int libavcodec_decompress_reconfigure(void *state, struct video_desc desc, + int rshift, int gshift, int bshift, int pitch, codec_t out_codec) +{ + struct state_libavcodec_decompress *s = + (struct state_libavcodec_decompress *) state; + + // assume that we have the same pitch as line size, for now + assert(pitch == vc_get_linesize(desc.width, out_codec)); + // TODO: add also RGB output codecs if possible + assert(out_codec == UYVY); + + s->pitch = pitch; + s->rshift = rshift; + s->gshift = gshift; + s->bshift = bshift; + + deconfigure(s); + configure_with(s, desc); + + s->max_compressed_len = 4 * desc.width * desc.height; + + return s->max_compressed_len; +} + +static void to_yuv422(char *dst_buffer, AVFrame *in_frame) +{ + for(int y = 0; y < (int) in_frame->height; ++y) { + char *src = (char *) in_frame->data[0] + in_frame->linesize[0] * y; + char *dst = (char *) dst_buffer + in_frame->width * y * 2; + for(int x = 0; x < in_frame->width; ++x) { + dst[x * 2 + 1] = src[x]; + } + } + + for(int y = 0; y < (int) in_frame->height / 2; ++y) { + char *src_cb = (char *) in_frame->data[1] + in_frame->linesize[1] * y; + char *src_cr = (char *) in_frame->data[2] + in_frame->linesize[2] * y; + char *dst1 = dst_buffer + in_frame->width * (y * 2) * 2; + char *dst2 = dst_buffer + (y * 2 + 1) * in_frame->width * 2; + for(int x = 0; x < in_frame->width / 2; ++x) { + dst1[x * 4] = src_cb[x]; + dst1[x * 4 + 2] = src_cr[x]; + dst2[x * 4] = src_cb[x]; + dst2[x * 4 + 2] = src_cr[x]; + } + } +} + +int libavcodec_decompress(void *state, unsigned char *dst, unsigned char *buffer, + unsigned int src_len, int frame_seq) +{ + struct state_libavcodec_decompress *s = (struct state_libavcodec_decompress *) state; + int len, got_frame; + int res = FALSE; + + s->pkt.size = src_len; + s->pkt.data = buffer; + + while (s->pkt.size > 0) { + len = avcodec_decode_video2(s->codec_ctx, s->frame, &got_frame, &s->pkt); + if(len < 0) { + fprintf(stderr, "[Libavcodec] Error while decoding frame.\n"); + return FALSE; + } + + if(got_frame) { + /* pass frame only if this is I-frame or we have complete + * GOP (assuming we are not using B-frames */ + if(s->frame->pict_type == AV_PICTURE_TYPE_I || + (s->frame->pict_type == AV_PICTURE_TYPE_P && + s->last_frame_seq == frame_seq - 1) + ) { + to_yuv422((char *) dst, s->frame); + s->last_frame_seq = frame_seq; + res = TRUE; + } else { + fprintf(stderr, "[Libavcodec] Missing appropriate I-frame " + "(last valid %d, this %d).\n", s->last_frame_seq, + frame_seq); + res = FALSE; + } + } + + if(s->pkt.data) { + s->pkt.size -= len; + s->pkt.data += len; + } + } + + return res; +} + +int libavcodec_decompress_get_property(void *state, int property, void *val, size_t *len) +{ + struct state_libavcodec_decompress *s = + (struct state_libavcodec_decompress *) state; + UNUSED(s); + int ret = FALSE; + + switch(property) { + case DECOMPRESS_PROPERTY_ACCEPTS_CORRUPTED_FRAME: + if(*len >= sizeof(int)) { + *(int *) val = FALSE; + *len = sizeof(int); + ret = TRUE; + } + break; + default: + ret = FALSE; + } + + return ret; +} + +void libavcodec_decompress_done(void *state) +{ + struct state_libavcodec_decompress *s = + (struct state_libavcodec_decompress *) state; + + deconfigure(s); + + free(s); +} + diff --git a/src/video_decompress/libavcodec.h b/src/video_decompress/libavcodec.h new file mode 100644 index 000000000..3517bc4da --- /dev/null +++ b/src/video_decompress/libavcodec.h @@ -0,0 +1,59 @@ +/* + * FILE: video_decompress/dxt_glsl.c + * AUTHORS: Martin Benes + * Lukas Hejtmanek + * Petr Holub + * Milos Liska + * Jiri Matela + * Dalibor Matura <255899@mail.muni.cz> + * Ian Wesley-Smith + * + * Copyright (c) 2005-2010 CESNET z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by CESNET z.s.p.o. + * + * 4. Neither the name of the CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "video_codec.h" + +#define LIBAVCODEC_MAGIC 0xff2f1b41u + +void *libavcodec_decompress_init(void); +int libavcodec_decompress_reconfigure(void *state, struct video_desc desc, + int rshift, int gshift, int bshift, int pitch, codec_t out_codec); +int libavcodec_decompress(void *state, unsigned char *dst, + unsigned char *buffer, unsigned int src_len, int frame_seq); +int libavcodec_decompress_get_property(void *state, int property, void *val, size_t *len); +void libavcodec_decompress_done(void *state); +