Author: miguel
Date: 2007-05-31 14:16:56 -0400 (Thu, 31 May 2007)
New Revision: 78364
Added:
trunk/moon/src/shape.cpp
trunk/moon/src/video.cpp
trunk/moon/src/videoff.cpp
Removed:
trunk/moon/src/video.cpp
Modified:
trunk/moon/src/Makefile.am
trunk/moon/src/runtime.cpp
Log:
Some reorg
Modified: trunk/moon/src/Makefile.am
===================================================================
--- trunk/moon/src/Makefile.am 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/Makefile.am 2007-05-31 18:16:56 UTC (rev 78364)
@@ -6,8 +6,9 @@
brush.cpp \
runtime.cpp \
runtime.h \
+ shape.cpp \
video.cpp \
- video.h
+ videoff.cpp
demo_SOURCES = \
demo.cpp
Modified: trunk/moon/src/runtime.cpp
===================================================================
--- trunk/moon/src/runtime.cpp 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/runtime.cpp 2007-05-31 18:16:56 UTC (rev 78364)
@@ -9,7 +9,7 @@
* See the LICENSE file included with the distribution for details.
*
*/
-
+#include <config.h>
#include <string.h>
#include <gtk/gtk.h>
#include <malloc.h>
@@ -246,102 +246,6 @@
}
void
-Shape::DoDraw (Surface *s, bool do_op)
-{
- if (fill){
- fill->SetupBrush (s->cairo);
- Draw (s);
- if (do_op)
- cairo_fill (s->cairo);
- }
-
- if (stroke){
- stroke->SetupBrush (s->cairo);
- Draw (s);
- if (do_op)
- cairo_stroke (s->cairo);
- }
-
-}
-
-void
-Shape::render (Surface *s, double *affine, int x, int y, int width, int height)
-{
- double result [6];
- double *matrix = item_get_affine (affine, xform, result);
-
- cairo_save (s->cairo);
- if (matrix != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) matrix);
-
- DoDraw (s, TRUE);
-
- cairo_restore (s->cairo);
-}
-
-void
-Shape::getbounds ()
-{
- double res [6];
- double *affine = item_affine_get_absolute (this, res);
-
- Surface *s = item_surface_get (this);
- if (s == NULL){
- // not yet attached
- return;
- }
-
- DoDraw (s, FALSE);
- cairo_stroke_extents (s->cairo, &x1, &y1, &x2, &y2);
- cairo_new_path (s->cairo);
-}
-
-void
-shape_set_fill (Shape *shape, Brush *fill)
-{
- if (shape->fill != NULL)
- brush_unref (shape->fill);
-
- shape->fill = brush_ref (fill);
-}
-
-void
-shape_set_stroke (Shape *shape, Brush *stroke)
-{
- if (shape->stroke != NULL)
- brush_unref (shape->stroke);
-
- shape->stroke = brush_ref (stroke);
-}
-
-void
-Rectangle::Draw (Surface *s)
-{
- cairo_rectangle (s->cairo, x, y, w, h);
-}
-
-Rectangle *
-rectangle_new (double x, double y, double w, double h)
-{
- Rectangle *rect = new Rectangle (x, y, w, h);
-
- return rect;
-}
-
-void
-Line::Draw (Surface *s)
-{
- cairo_move_to (s->cairo, line_x1, line_x2);
- cairo_line_to (s->cairo, line_x2, line_y2);
-}
-
-Line *
-line_new (double x1, double y1, double x2, double y2)
-{
- return new Line (x1, y1, x2, y2);
-}
-
-void
surface_clear (Surface *s, int x, int y, int width, int height)
{
if (x < 0){
Added: trunk/moon/src/shape.cpp
===================================================================
--- trunk/moon/src/shape.cpp 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/shape.cpp 2007-05-31 18:16:56 UTC (rev 78364)
@@ -0,0 +1,115 @@
+/*
+ * runtime.cpp: Core surface and canvas definitions.
+ *
+ * Author:
+ * Miguel de Icaza ([EMAIL PROTECTED])
+ *
+ * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ *
+ * See the LICENSE file included with the distribution for details.
+ *
+ */
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <malloc.h>
+#include <glib.h>
+#include <stdlib.h>
+#include "runtime.h"
+
+void
+Shape::DoDraw (Surface *s, bool do_op)
+{
+ if (fill){
+ fill->SetupBrush (s->cairo);
+ Draw (s);
+ if (do_op)
+ cairo_fill (s->cairo);
+ }
+
+ if (stroke){
+ stroke->SetupBrush (s->cairo);
+ Draw (s);
+ if (do_op)
+ cairo_stroke (s->cairo);
+ }
+
+}
+
+void
+Shape::render (Surface *s, double *affine, int x, int y, int width, int height)
+{
+ double result [6];
+ double *matrix = item_get_affine (affine, xform, result);
+
+ cairo_save (s->cairo);
+ if (matrix != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) matrix);
+
+ DoDraw (s, TRUE);
+
+ cairo_restore (s->cairo);
+}
+
+void
+Shape::getbounds ()
+{
+ double res [6];
+ double *affine = item_affine_get_absolute (this, res);
+
+ Surface *s = item_surface_get (this);
+ if (s == NULL){
+ // not yet attached
+ return;
+ }
+
+ DoDraw (s, FALSE);
+ cairo_stroke_extents (s->cairo, &x1, &y1, &x2, &y2);
+ cairo_new_path (s->cairo);
+}
+
+void
+shape_set_fill (Shape *shape, Brush *fill)
+{
+ if (shape->fill != NULL)
+ brush_unref (shape->fill);
+
+ shape->fill = brush_ref (fill);
+}
+
+void
+shape_set_stroke (Shape *shape, Brush *stroke)
+{
+ if (shape->stroke != NULL)
+ brush_unref (shape->stroke);
+
+ shape->stroke = brush_ref (stroke);
+}
+
+void
+Rectangle::Draw (Surface *s)
+{
+ cairo_rectangle (s->cairo, x, y, w, h);
+}
+
+Rectangle *
+rectangle_new (double x, double y, double w, double h)
+{
+ Rectangle *rect = new Rectangle (x, y, w, h);
+
+ return rect;
+}
+
+void
+Line::Draw (Surface *s)
+{
+ cairo_move_to (s->cairo, line_x1, line_x2);
+ cairo_line_to (s->cairo, line_x2, line_y2);
+}
+
+Line *
+line_new (double x1, double y1, double x2, double y2)
+{
+ return new Line (x1, y1, x2, y2);
+}
+
Property changes on: trunk/moon/src/shape.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Deleted: trunk/moon/src/video.cpp
===================================================================
--- trunk/moon/src/video.cpp 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/video.cpp 2007-05-31 18:16:56 UTC (rev 78364)
@@ -1,537 +0,0 @@
-/*
- * video.c: The moonlight's video item
- *
- * Author:
- * Miguel de Icaza ([EMAIL PROTECTED])
- *
- * Copyright 2007 Novell, Inc. (http://www.novell.com)
- *
- * See the LICENSE file included with the distribution for details.
- *
- */
-#define __STDC_CONSTANT_MACROS
-#include <unistd.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include <cairo.h>
-#include <malloc.h>
-#include <glib.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <errno.h>
-#include "runtime.h"
-
-//
-#include <fcntl.h>
-
-// video specific
-G_BEGIN_DECLS
-#include <avformat.h>
-#include <stdint.h>
-#include <avcodec.h>
-#include <swscale.h>
-G_END_DECLS
-
-#include <sys/time.h>
-
-// Alsa
-#include <asoundlib.h>
-
-Video::Video (const char *filename, double x, double y)
-{
- this->filename = g_strdup (filename);
- this->x = x;
- this->y = y;
-}
-
-Video::~Video ()
-{
- g_free (filename);
-}
-
-typedef enum {
- // When a new frame is ready
- CMD_NEWFRAME,
-
- // When the video has been loaded, this updates the bounding box in the
Gtk+ GUI thread.
- CMD_INITED
-} GuiCommand;
-
-class VideoFfmpeg : public Video {
-public:
- int w, h;
-
- VideoFfmpeg (const char *filename, double x, double y);
- ~VideoFfmpeg ();
-
- // Virtual method overrides
- void getbounds ();
- void render (Surface *s, double *affine, int x, int y, int width, int
height);
-
-
- GThread *decode_thread_id;
- int pipes [2];
- GIOChannel *pipe_channel;
-
- //
- // AV Fields
- //
- AVFormatContext *av_format_context;
-
- // Video
- AVCodec *video_codec;
- AVStream *video_stream;
- int video_stream_idx;
- GMutex *video_mutex;
- AVStream *audio_stream;
- int audio_stream_idx;
-
- snd_pcm_t *pcm;
-
- // The decoding thread will wait until the main thread reads our
CMD_INITED
- // command
- GCond *cond_decode_thread_proceed;
-
- GAsyncQueue *video_frames;
-
- //
- // Timing components, the next time to display.
- //
- double micro_to_pts; // how to convert microseconds to pts
(pts units are in video_stream->time_base units)
- uint64_t play_start_time; // Playback starts at this host time.
- uint64_t initial_pts; // The PTS of the first frame when we
started playing
- int frame_size; // microseconds between frames.
- guint timeout_handle;
-
- //
- // The components used to render the buffer
- //
-
- // The buffer where we store the RGB data
- unsigned char *video_rgb_buffer;
-
- // The cairo surface that represents video_rgb_buffer
- cairo_surface_t *video_cairo_surface;
-
- // The SwsContext image convertor
- struct SwsContext *video_scale_context;
-
-};
-
-static GMutex *video_lock;
-
-void
-VideoFfmpeg::getbounds ()
-{
- double res [6];
- double *affine = item_affine_get_absolute (this, res);
- AVCodecContext *cc;
-
- Surface *s = item_surface_get (this);
- if (s == NULL){
- // not yet attached
- return;
- }
-
- // If we have not been initialized yet, we cant compute the bounds
- if (video_stream == NULL || video_stream->codec == NULL){
- x1 = y1 = x2 = y2 = 0;
- return;
- }
- cc = video_stream->codec;
-
- cairo_save (s->cairo);
- if (affine != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) affine);
-
- cairo_rectangle (s->cairo, x, y, x + cc->width, y + cc->height);
- cairo_fill_extents (s->cairo, &x1, &y1, &x2, &y2);
-
- cairo_restore (s->cairo);
-}
-
-static void
-send_command (VideoFfmpeg *video, uint8_t cmd)
-{
- int ret;
-
- do {
- ret = write (video->pipes [1], &cmd, 1);
- } while (ret == -1 && errno == EINTR);
-}
-
-//
-// The decoder thread, runs until complete
-//
-static gpointer
-decoder (gpointer data)
-{
- VideoFfmpeg *video = (VideoFfmpeg *) data;
- AVFrame *video_frame;
- int i;
- int video_stream_idx, audio_stream_idx;
- AVFormatParameters pars;
-
- printf ("Video THREAD STARTS\n");
-
- g_mutex_lock (video_lock);
- // avcodec setup
- // auto-detect format
- // default buffer size
- // no additional parameters
- memset (&pars, 0, sizeof (pars));
-
- if (av_open_input_file (&video->av_format_context, video->filename,
NULL, 0, NULL) < 0){
- g_error ("ERROR: open input file\n");
- }
- if (video->av_format_context == (void *) &rtsp_demuxer){
- g_error ("ERROR: stream info\n");
- }
-
- av_read_play (video->av_format_context);
-
- if (av_find_stream_info(video->av_format_context) < 0){
- g_error ("ERROR: stream info\n");
- }
-
- for (i = 0; i < video->av_format_context->nb_streams; i++){
- AVStream *stream = video->av_format_context->streams [i];
- AVCodecContext *cc;
- int n;
-
- switch (stream->codec->codec_type){
- case CODEC_TYPE_AUDIO:
- video->audio_stream_idx = i;
- video->audio_stream = video->av_format_context->streams
[i];
- audio_stream_idx = video->audio_stream_idx;
- cc = video->audio_stream->codec;
- n = snd_pcm_open (&video->pcm, "default",
SND_PCM_STREAM_PLAYBACK, 0);
- if (n < 0)
- printf ("Error, can not initialize audio\n");
- else {
- snd_pcm_set_params (
- video->pcm,
- SND_PCM_FORMAT_S16,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- cc->channels,
- cc->sample_rate,
- 1, 0);
- }
- break;
-
- case CODEC_TYPE_VIDEO:
- video->video_codec = avcodec_find_decoder
(stream->codec->codec_id);
- video->video_stream = video->av_format_context->streams
[i];
- avcodec_open (video->video_stream->codec,
video->video_codec);
- video->video_stream_idx = i;
- video_stream_idx = i;
-
- // Allocate our rendering buffer and cairo surface
- //
- cc = video->video_stream->codec;
- video->video_rgb_buffer = (unsigned char *) malloc
(cc->width * cc->height * 4);
-
- video->video_cairo_surface =
cairo_image_surface_create_for_data (
- video->video_rgb_buffer, CAIRO_FORMAT_ARGB32,
- cc->width, cc->height, cc->width * 4);
-
- // The YUB to RGB scaleter/translator
- printf ("Pix_fmt=%d\n", cc->pix_fmt);
- video->video_scale_context = sws_getContext (
- cc->width, cc->height, cc->pix_fmt, cc->width,
cc->height,
- PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
- break;
- }
- }
-
- //
- // Notify the main thread that we are done loading the video, and that
it
- // can pull the width/height information out of this video
- //
- send_command (video, CMD_INITED);
- g_cond_wait (video->cond_decode_thread_proceed, video_lock);
- g_mutex_unlock (video_lock);
-
- while (TRUE){
- AVPacket packet, *pkt = &packet;
-
- int ret;
-
- ret = av_read_frame (video->av_format_context, pkt);
- if (ret == -1){
- printf ("Failed to read frame, terminating\n");
- break;
- }
-
- if (pkt->stream_index == audio_stream_idx){
- //snd_pcm_writei (video->pcm,
- }
-
- if (pkt->stream_index == video_stream_idx){
- int got_picture, n;
- static int count;
-
- video_frame = avcodec_alloc_frame ();
-
- while (g_async_queue_length (video->video_frames) > 10)
- g_usleep (1000);
-
- //printf ("pkt=%7.2f %8d\r\n", (double) pkt->pts,
count++);
- //fflush (stdout);
- avcodec_decode_video (video->video_stream->codec,
video_frame, &got_picture, pkt->data, pkt->size);
-
- if (got_picture){
- // Copy the PTS here, dont know why decode
video does not set it
- if (video_frame->pts == 0)
- video_frame->pts = pkt->pts;
-
- //printf ("Adding PTS=%ld\n", video_frame->pts);
- g_mutex_lock (video->video_mutex);
- send_command (video, CMD_NEWFRAME);
- g_async_queue_push (video->video_frames,
video_frame);
- g_mutex_unlock (video->video_mutex);
- }
- }
- av_free_packet (pkt);
- }
-
- printf ("THREAD COMPLETES\n");
-
- return NULL;
-}
-
-//
-// Convert the AVframe into an RGB buffer we can render
-//
-void
-convert_to_rgb (VideoFfmpeg *video, AVFrame *frame)
-{
- static struct SwsContext *img_convert_ctx;
- AVCodecContext *cc = video->video_stream->codec;
- AVPicture pict;
- uint8_t *rgb_dest[3] = { video->video_rgb_buffer, NULL, NULL};
- int rgb_stride [3] = { cc->width * 4, 0, 0 };
-
- sws_scale (video->video_scale_context, frame->data, frame->linesize, 0,
- video->video_stream->codec->height, rgb_dest, rgb_stride);
-}
-
-static gboolean
-load_next_frame (gpointer data)
-{
- VideoFfmpeg *video = (VideoFfmpeg *) data;
- uint64_t target_pts = (uint64_t) ((av_gettime () -
video->play_start_time) * video->micro_to_pts) + video->initial_pts;
- gboolean cont = TRUE;
- AVFrame *frame = NULL;
-
- g_mutex_lock (video->video_mutex);
-
- while (g_async_queue_length (video->video_frames) != 0){
- void *rgb;
- uint64_t diff;
-
- frame = (AVFrame *) g_async_queue_pop (video->video_frames);
-
- if (frame->pts + video->frame_size < target_pts){
-
- // We got more stuff on the queue, discard this one
- if (g_async_queue_length (video->video_frames) > 1){
- printf ("discarding frame at time=%g seconds
because it is late\n",
- (frame->pts -
video->video_stream->start_time) *
- av_q2d
(video->video_stream->time_base));
- av_free (frame);
- } else {
- printf ("Going to use the frame, and stopping
stop\n");
-
- // We do not have anything else, render this
last frame, and
- // stop the timer.
- cont = FALSE;
- video->timeout_handle = 0;
- break;
- }
- } else {
- // Got a frame in the proper time, break the loop, and
continue.
- break;
- }
-
- }
- g_mutex_unlock (video->video_mutex);
-
- if (frame != NULL){
- convert_to_rgb (video, frame);
- av_free (frame);
- }
-
- item_invalidate ((Item *) video);
-
- //gdk_window_process_updates (get_surface
(video)->drawing_area->window, FALSE);
-
- return cont;
-}
-
-void
-restart_timer (VideoFfmpeg *video)
-{
- //video->frame_size = (int) (1000 * av_q2d
(video->video_stream->time_base) * av_q2d (video->video_stream->r_frame_rate));
-
- // microseconds it takes to advance to the next frame
- video->frame_size = 1000 / av_q2d (video->video_stream->r_frame_rate);
-
- video->play_start_time = av_gettime ();
- printf ("Adding timer for %d\n", video->frame_size);
- video->timeout_handle = g_timeout_add (video->frame_size,
load_next_frame, video);
-}
-
-//
-// Called on the GUI thread when we got a frame to render.
-//
-gboolean
-video_ready (GIOChannel *source, GIOCondition condition, gpointer data)
-{
- VideoFfmpeg *video = (VideoFfmpeg *) data;
- AVFrame *frame;
- int8_t commands [32];
- int ret, i;
-
- do {
- ret = read (video->pipes [0], &commands, sizeof (commands));
- } while ((ret == -1 && ret == EINTR));
-
- // Nothing.
- if (ret <= 0)
- return TRUE;
-
- for (i = 0; i < ret; i++){
- switch (commands [i]){
- case CMD_NEWFRAME:
- if (video->timeout_handle == 0)
- restart_timer (video);
- break;
-
- case CMD_INITED:
- // Track where we are at now
- video->initial_pts = video->video_stream->start_time;
- restart_timer (video);
-
- //
- // Poor man's [play] command.
- //
- g_cond_signal (video->cond_decode_thread_proceed);
-
- item_update_bounds ((Item *) video);
- video->w = video->video_stream->codec->width;
- video->h = video->video_stream->codec->height;
- break;
-
- default:
- fprintf (stderr, "video_ready: unknown command from
decoding thread (%d)\n", commands [i]);
- }
- }
-
- return TRUE;
-}
-
-void
-VideoFfmpeg::render (Surface *s, double *affine, int x, int y, int width, int
height)
-{
- double actual [6];
- double *use_affine = item_get_affine (affine, xform, actual);
- cairo_pattern_t *pattern, *old_pattern;
-
- cairo_save (s->cairo);
- if (use_affine != NULL)
- cairo_set_matrix (s->cairo, (cairo_matrix_t *) use_affine);
-
- cairo_translate (s->cairo, this->x, this->y);
- cairo_set_source_surface (s->cairo, video_cairo_surface, 0, 0);
- cairo_paint (s->cairo);
-
- cairo_rectangle (s->cairo, x, y, w, h);
-
- cairo_restore (s->cairo);
-}
-
-static int ffmpeg_inited;
-static snd_pcm_t *pcm;
-
-static void
-ffmpeg_init ()
-{
- if (!ffmpeg_inited){
-
- video_lock = g_mutex_new ();
- avcodec_init ();
- av_register_all ();
- avcodec_register_all ();
- ffmpeg_inited = TRUE;
- }
-}
-Video *
-video_ffmpeg_new (const char *filename, double x, double y)
-{
- VideoFfmpeg *video;
-
- ffmpeg_init ();
-
- video = new VideoFfmpeg (filename, x, y);
-
- return (Video *) video;
-}
-
-VideoFfmpeg::VideoFfmpeg (const char *filename, double x, double y) : Video
(filename, x, y)
-{
- video_codec = NULL;
- video_stream = NULL;
- video_rgb_buffer = NULL;
- video_cairo_surface = NULL;
- video_scale_context = NULL;
- av_format_context = NULL;
-
- timeout_handle = 0;
- initial_pts = 0;
- play_start_time = 0;
- frame_size = 0;
-
-
- pipe (pipes);
- fcntl (pipes [0], F_SETFL, O_NONBLOCK);
-
- video_mutex = g_mutex_new ();
- video_frames = g_async_queue_new ();
- pipe_channel = g_io_channel_unix_new (pipes [0]);
- g_io_add_watch (pipe_channel, G_IO_IN, video_ready, this);
-
- cond_decode_thread_proceed = g_cond_new ();
-
- decode_thread_id = g_thread_create (decoder, this, TRUE, NULL);
-}
-
-VideoFfmpeg::~VideoFfmpeg ()
-{
- // TODO:
- // * Ask our thread to shutdown nicely.
- //
- fprintf (stderr, "We should stop the thread");
-
- g_async_queue_unref (video_frames);
- g_mutex_free (video_mutex);
- close (pipes [0]);
- close (pipes [1]);
- g_io_channel_close (pipe_channel);
- g_free (video_rgb_buffer);
- cairo_surface_destroy (video_cairo_surface);
-}
-
-
-/*
- * video_new:
- * @filename:
- * @x, @y: Position where to position the video
- *
- */
-Video *
-video_new (const char *filename, double x, double y)
-{
- return video_ffmpeg_new (filename, x, y);
-}
-
Added: trunk/moon/src/video.cpp
===================================================================
--- trunk/moon/src/video.cpp 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/video.cpp 2007-05-31 18:16:56 UTC (rev 78364)
@@ -0,0 +1,31 @@
+/*
+ * video.c: The moonlight's video item
+ *
+ * Author:
+ * Miguel de Icaza ([EMAIL PROTECTED])
+ *
+ * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ *
+ * See the LICENSE file included with the distribution for details.
+ *
+ */
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include "runtime.h"
+
+Video::Video (const char *filename, double x, double y)
+{
+ this->filename = g_strdup (filename);
+ this->x = x;
+ this->y = y;
+}
+
+Video::~Video ()
+{
+ g_free (filename);
+}
+
Property changes on: trunk/moon/src/video.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Copied: trunk/moon/src/videoff.cpp (from rev 78363, trunk/moon/src/video.cpp)
===================================================================
--- trunk/moon/src/video.cpp 2007-05-31 18:13:00 UTC (rev 78363)
+++ trunk/moon/src/videoff.cpp 2007-05-31 18:16:56 UTC (rev 78364)
@@ -0,0 +1,526 @@
+/*
+ * video.c: The moonlight's video item
+ *
+ * Author:
+ * Miguel de Icaza ([EMAIL PROTECTED])
+ *
+ * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ *
+ * See the LICENSE file included with the distribution for details.
+ *
+ */
+#define __STDC_CONSTANT_MACROS
+#include <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <malloc.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include "runtime.h"
+
+//
+#include <fcntl.h>
+
+// video specific
+G_BEGIN_DECLS
+#include <avformat.h>
+#include <stdint.h>
+#include <avcodec.h>
+#include <swscale.h>
+G_END_DECLS
+
+#include <sys/time.h>
+
+// Alsa
+#include <asoundlib.h>
+
+typedef enum {
+ // When a new frame is ready
+ CMD_NEWFRAME,
+
+ // When the video has been loaded, this updates the bounding box in the
Gtk+ GUI thread.
+ CMD_INITED
+} GuiCommand;
+
+class VideoFfmpeg : public Video {
+public:
+ int w, h;
+
+ VideoFfmpeg (const char *filename, double x, double y);
+ ~VideoFfmpeg ();
+
+ // Virtual method overrides
+ void getbounds ();
+ void render (Surface *s, double *affine, int x, int y, int width, int
height);
+
+
+ GThread *decode_thread_id;
+ int pipes [2];
+ GIOChannel *pipe_channel;
+
+ //
+ // AV Fields
+ //
+ AVFormatContext *av_format_context;
+
+ // Video
+ AVCodec *video_codec;
+ AVStream *video_stream;
+ int video_stream_idx;
+ GMutex *video_mutex;
+ AVStream *audio_stream;
+ int audio_stream_idx;
+
+ snd_pcm_t *pcm;
+
+ // The decoding thread will wait until the main thread reads our
CMD_INITED
+ // command
+ GCond *cond_decode_thread_proceed;
+
+ GAsyncQueue *video_frames;
+
+ //
+ // Timing components, the next time to display.
+ //
+ double micro_to_pts; // how to convert microseconds to pts
(pts units are in video_stream->time_base units)
+ uint64_t play_start_time; // Playback starts at this host time.
+ uint64_t initial_pts; // The PTS of the first frame when we
started playing
+ int frame_size; // microseconds between frames.
+ guint timeout_handle;
+
+ //
+ // The components used to render the buffer
+ //
+
+ // The buffer where we store the RGB data
+ unsigned char *video_rgb_buffer;
+
+ // The cairo surface that represents video_rgb_buffer
+ cairo_surface_t *video_cairo_surface;
+
+ // The SwsContext image convertor
+ struct SwsContext *video_scale_context;
+
+};
+
+static GMutex *video_lock;
+
+void
+VideoFfmpeg::getbounds ()
+{
+ double res [6];
+ double *affine = item_affine_get_absolute (this, res);
+ AVCodecContext *cc;
+
+ Surface *s = item_surface_get (this);
+ if (s == NULL){
+ // not yet attached
+ return;
+ }
+
+ // If we have not been initialized yet, we cant compute the bounds
+ if (video_stream == NULL || video_stream->codec == NULL){
+ x1 = y1 = x2 = y2 = 0;
+ return;
+ }
+ cc = video_stream->codec;
+
+ cairo_save (s->cairo);
+ if (affine != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) affine);
+
+ cairo_rectangle (s->cairo, x, y, x + cc->width, y + cc->height);
+ cairo_fill_extents (s->cairo, &x1, &y1, &x2, &y2);
+
+ cairo_restore (s->cairo);
+}
+
+static void
+send_command (VideoFfmpeg *video, uint8_t cmd)
+{
+ int ret;
+
+ do {
+ ret = write (video->pipes [1], &cmd, 1);
+ } while (ret == -1 && errno == EINTR);
+}
+
+//
+// The decoder thread, runs until complete
+//
+static gpointer
+decoder (gpointer data)
+{
+ VideoFfmpeg *video = (VideoFfmpeg *) data;
+ AVFrame *video_frame;
+ int i;
+ int video_stream_idx, audio_stream_idx;
+ AVFormatParameters pars;
+
+ printf ("Video THREAD STARTS\n");
+
+ g_mutex_lock (video_lock);
+ // avcodec setup
+ // auto-detect format
+ // default buffer size
+ // no additional parameters
+ memset (&pars, 0, sizeof (pars));
+
+ if (av_open_input_file (&video->av_format_context, video->filename,
NULL, 0, NULL) < 0){
+ g_error ("ERROR: open input file\n");
+ }
+ if (video->av_format_context == (void *) &rtsp_demuxer){
+ g_error ("ERROR: stream info\n");
+ }
+
+ av_read_play (video->av_format_context);
+
+ if (av_find_stream_info(video->av_format_context) < 0){
+ g_error ("ERROR: stream info\n");
+ }
+
+ for (i = 0; i < video->av_format_context->nb_streams; i++){
+ AVStream *stream = video->av_format_context->streams [i];
+ AVCodecContext *cc;
+ int n;
+
+ switch (stream->codec->codec_type){
+ case CODEC_TYPE_AUDIO:
+ video->audio_stream_idx = i;
+ video->audio_stream = video->av_format_context->streams
[i];
+ audio_stream_idx = video->audio_stream_idx;
+ cc = video->audio_stream->codec;
+ n = snd_pcm_open (&video->pcm, "default",
SND_PCM_STREAM_PLAYBACK, 0);
+ if (n < 0)
+ printf ("Error, can not initialize audio\n");
+ else {
+ snd_pcm_set_params (
+ video->pcm,
+ SND_PCM_FORMAT_S16,
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ cc->channels,
+ cc->sample_rate,
+ 1, 0);
+ }
+ break;
+
+ case CODEC_TYPE_VIDEO:
+ video->video_codec = avcodec_find_decoder
(stream->codec->codec_id);
+ video->video_stream = video->av_format_context->streams
[i];
+ avcodec_open (video->video_stream->codec,
video->video_codec);
+ video->video_stream_idx = i;
+ video_stream_idx = i;
+
+ // Allocate our rendering buffer and cairo surface
+ //
+ cc = video->video_stream->codec;
+ video->video_rgb_buffer = (unsigned char *) malloc
(cc->width * cc->height * 4);
+
+ video->video_cairo_surface =
cairo_image_surface_create_for_data (
+ video->video_rgb_buffer, CAIRO_FORMAT_ARGB32,
+ cc->width, cc->height, cc->width * 4);
+
+ // The YUB to RGB scaleter/translator
+ printf ("Pix_fmt=%d\n", cc->pix_fmt);
+ video->video_scale_context = sws_getContext (
+ cc->width, cc->height, cc->pix_fmt, cc->width,
cc->height,
+ PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
+ break;
+ }
+ }
+
+ //
+ // Notify the main thread that we are done loading the video, and that
it
+ // can pull the width/height information out of this video
+ //
+ send_command (video, CMD_INITED);
+ g_cond_wait (video->cond_decode_thread_proceed, video_lock);
+ g_mutex_unlock (video_lock);
+
+ while (TRUE){
+ AVPacket packet, *pkt = &packet;
+
+ int ret;
+
+ ret = av_read_frame (video->av_format_context, pkt);
+ if (ret == -1){
+ printf ("Failed to read frame, terminating\n");
+ break;
+ }
+
+ if (pkt->stream_index == audio_stream_idx){
+ //snd_pcm_writei (video->pcm,
+ }
+
+ if (pkt->stream_index == video_stream_idx){
+ int got_picture, n;
+ static int count;
+
+ video_frame = avcodec_alloc_frame ();
+
+ while (g_async_queue_length (video->video_frames) > 10)
+ g_usleep (1000);
+
+ //printf ("pkt=%7.2f %8d\r\n", (double) pkt->pts,
count++);
+ //fflush (stdout);
+ avcodec_decode_video (video->video_stream->codec,
video_frame, &got_picture, pkt->data, pkt->size);
+
+ if (got_picture){
+ // Copy the PTS here, dont know why decode
video does not set it
+ if (video_frame->pts == 0)
+ video_frame->pts = pkt->pts;
+
+ //printf ("Adding PTS=%ld\n", video_frame->pts);
+ g_mutex_lock (video->video_mutex);
+ send_command (video, CMD_NEWFRAME);
+ g_async_queue_push (video->video_frames,
video_frame);
+ g_mutex_unlock (video->video_mutex);
+ }
+ }
+ av_free_packet (pkt);
+ }
+
+ printf ("THREAD COMPLETES\n");
+
+ return NULL;
+}
+
+//
+// Convert the AVframe into an RGB buffer we can render
+//
+void
+convert_to_rgb (VideoFfmpeg *video, AVFrame *frame)
+{
+ static struct SwsContext *img_convert_ctx;
+ AVCodecContext *cc = video->video_stream->codec;
+ AVPicture pict;
+ uint8_t *rgb_dest[3] = { video->video_rgb_buffer, NULL, NULL};
+ int rgb_stride [3] = { cc->width * 4, 0, 0 };
+
+ sws_scale (video->video_scale_context, frame->data, frame->linesize, 0,
+ video->video_stream->codec->height, rgb_dest, rgb_stride);
+}
+
+static gboolean
+load_next_frame (gpointer data)
+{
+ VideoFfmpeg *video = (VideoFfmpeg *) data;
+ uint64_t target_pts = (uint64_t) ((av_gettime () -
video->play_start_time) * video->micro_to_pts) + video->initial_pts;
+ gboolean cont = TRUE;
+ AVFrame *frame = NULL;
+
+ g_mutex_lock (video->video_mutex);
+
+ while (g_async_queue_length (video->video_frames) != 0){
+ void *rgb;
+ uint64_t diff;
+
+ frame = (AVFrame *) g_async_queue_pop (video->video_frames);
+
+ if (frame->pts + video->frame_size < target_pts){
+
+ // We got more stuff on the queue, discard this one
+ if (g_async_queue_length (video->video_frames) > 1){
+ printf ("discarding frame at time=%g seconds
because it is late\n",
+ (frame->pts -
video->video_stream->start_time) *
+ av_q2d
(video->video_stream->time_base));
+ av_free (frame);
+ } else {
+ printf ("Going to use the frame, and stopping
stop\n");
+
+ // We do not have anything else, render this
last frame, and
+ // stop the timer.
+ cont = FALSE;
+ video->timeout_handle = 0;
+ break;
+ }
+ } else {
+ // Got a frame in the proper time, break the loop, and
continue.
+ break;
+ }
+
+ }
+ g_mutex_unlock (video->video_mutex);
+
+ if (frame != NULL){
+ convert_to_rgb (video, frame);
+ av_free (frame);
+ }
+
+ item_invalidate ((Item *) video);
+
+ //gdk_window_process_updates (get_surface
(video)->drawing_area->window, FALSE);
+
+ return cont;
+}
+
+void
+restart_timer (VideoFfmpeg *video)
+{
+ //video->frame_size = (int) (1000 * av_q2d
(video->video_stream->time_base) * av_q2d (video->video_stream->r_frame_rate));
+
+ // microseconds it takes to advance to the next frame
+ video->frame_size = 1000 / av_q2d (video->video_stream->r_frame_rate);
+
+ video->play_start_time = av_gettime ();
+ printf ("Adding timer for %d\n", video->frame_size);
+ video->timeout_handle = g_timeout_add (video->frame_size,
load_next_frame, video);
+}
+
+//
+// Called on the GUI thread when we got a frame to render.
+//
+gboolean
+video_ready (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ VideoFfmpeg *video = (VideoFfmpeg *) data;
+ AVFrame *frame;
+ int8_t commands [32];
+ int ret, i;
+
+ do {
+ ret = read (video->pipes [0], &commands, sizeof (commands));
+ } while ((ret == -1 && ret == EINTR));
+
+ // Nothing.
+ if (ret <= 0)
+ return TRUE;
+
+ for (i = 0; i < ret; i++){
+ switch (commands [i]){
+ case CMD_NEWFRAME:
+ if (video->timeout_handle == 0)
+ restart_timer (video);
+ break;
+
+ case CMD_INITED:
+ // Track where we are at now
+ video->initial_pts = video->video_stream->start_time;
+ restart_timer (video);
+
+ //
+ // Poor man's [play] command.
+ //
+ g_cond_signal (video->cond_decode_thread_proceed);
+
+ item_update_bounds ((Item *) video);
+ video->w = video->video_stream->codec->width;
+ video->h = video->video_stream->codec->height;
+ break;
+
+ default:
+ fprintf (stderr, "video_ready: unknown command from
decoding thread (%d)\n", commands [i]);
+ }
+ }
+
+ return TRUE;
+}
+
+void
+VideoFfmpeg::render (Surface *s, double *affine, int x, int y, int width, int
height)
+{
+ double actual [6];
+ double *use_affine = item_get_affine (affine, xform, actual);
+ cairo_pattern_t *pattern, *old_pattern;
+
+ cairo_save (s->cairo);
+ if (use_affine != NULL)
+ cairo_set_matrix (s->cairo, (cairo_matrix_t *) use_affine);
+
+ cairo_translate (s->cairo, this->x, this->y);
+ cairo_set_source_surface (s->cairo, video_cairo_surface, 0, 0);
+ cairo_paint (s->cairo);
+
+ cairo_rectangle (s->cairo, x, y, w, h);
+
+ cairo_restore (s->cairo);
+}
+
+static int ffmpeg_inited;
+static snd_pcm_t *pcm;
+
+static void
+ffmpeg_init ()
+{
+ if (!ffmpeg_inited){
+
+ video_lock = g_mutex_new ();
+ avcodec_init ();
+ av_register_all ();
+ avcodec_register_all ();
+ ffmpeg_inited = TRUE;
+ }
+}
+Video *
+video_ffmpeg_new (const char *filename, double x, double y)
+{
+ VideoFfmpeg *video;
+
+ ffmpeg_init ();
+
+ video = new VideoFfmpeg (filename, x, y);
+
+ return (Video *) video;
+}
+
+VideoFfmpeg::VideoFfmpeg (const char *filename, double x, double y) : Video
(filename, x, y)
+{
+ video_codec = NULL;
+ video_stream = NULL;
+ video_rgb_buffer = NULL;
+ video_cairo_surface = NULL;
+ video_scale_context = NULL;
+ av_format_context = NULL;
+
+ timeout_handle = 0;
+ initial_pts = 0;
+ play_start_time = 0;
+ frame_size = 0;
+
+
+ pipe (pipes);
+ fcntl (pipes [0], F_SETFL, O_NONBLOCK);
+
+ video_mutex = g_mutex_new ();
+ video_frames = g_async_queue_new ();
+ pipe_channel = g_io_channel_unix_new (pipes [0]);
+ g_io_add_watch (pipe_channel, G_IO_IN, video_ready, this);
+
+ cond_decode_thread_proceed = g_cond_new ();
+
+ decode_thread_id = g_thread_create (decoder, this, TRUE, NULL);
+}
+
+VideoFfmpeg::~VideoFfmpeg ()
+{
+ // TODO:
+ // * Ask our thread to shutdown nicely.
+ //
+ fprintf (stderr, "We should stop the thread");
+
+ g_async_queue_unref (video_frames);
+ g_mutex_free (video_mutex);
+ close (pipes [0]);
+ close (pipes [1]);
+ g_io_channel_close (pipe_channel);
+ g_free (video_rgb_buffer);
+ cairo_surface_destroy (video_cairo_surface);
+}
+
+
+/*
+ * video_new:
+ * @filename:
+ * @x, @y: Position where to position the video
+ *
+ */
+Video *
+video_new (const char *filename, double x, double y)
+{
+ return video_ffmpeg_new (filename, x, y);
+}
+
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches