網頁

顯示具有 git 標籤的文章。 顯示所有文章
顯示具有 git 標籤的文章。 顯示所有文章

2022年8月1日 星期一

VLC 3.0.17.4 之 LIBVLC 新增錄影功能

參考 Add new API to libvlc to record the current media to either an application-specified file or a file automatically created by vlc

建立 git patch
$ sudo git diff HEAD
$ sudo git status
$ sudo git diff HEAD include/vlc/libvlc_events.h
$ sudo git add include/vlc/libvlc_events.h
$ sudo git diff HEAD include/vlc/libvlc_media_player.h
$ sudo git add include/vlc/libvlc_media_player.h
$ sudo git diff HEAD lib/event.c
$ sudo git add lib/event.c
$ sudo git diff HEAD lib/libvlc.sym
$ sudo git add lib/libvlc.sym
$ sudo git diff HEAD lib/media_player.c
$ sudo git add lib/media_player.c
$ sudo git diff HEAD modules/logger/file.c
$ sudo git add modules/logger/file.c
$ sudo git diff HEAD modules/spu/marq.c
$ sudo git add modules/spu/marq.c
$ sudo git diff HEAD modules/stream_out/record.c
$ sudo git add modules/stream_out/record.c
$ sudo git diff HEAD src/input/input.c
$ sudo git add src/input/input.c
$ sudo git diff HEAD src/input/var.c
$ sudo git add src/input/var.c
$ sudo git diff HEAD src/text/strings.c
$ sudo git add src/text/strings.c
$ sudo git diff HEAD src/video_output/vout_subpictures.c
$ sudo git add src/video_output/vout_subpictures.c

$ sudo git commit -m "add record function"
$ sudo git format-patch -1 -o ../patch
$ sudo git apply ../patch/0001-libvlc-add-record-function.patch

$ sudo git log
$ sudo git checkout c650ce1a4e352c

=======
From 6917e3862fdbd2ae7b411ca6d15ce15644b4e5c8 Mon Sep 17 00:00:00 2001
From: root <ingrenn@yahoo.com.tw>
Date: Thu, 4 Aug 2022 11:33:50 +0800
Subject: [PATCH] libvlc add record function

---
 include/vlc/libvlc_events.h         |  10 +++
 include/vlc/libvlc_media_player.h   | 115 ++++++++++++++++++++++++++++
 lib/event.c                         |   3 +
 lib/libvlc.sym                      |   4 +
 lib/media_player.c                  | 114 ++++++++++++++++++++++++++-
 modules/logger/file.c               |  13 +++-
 modules/spu/marq.c                  |   1 +
 modules/stream_out/record.c         |  20 +++++
 src/input/input.c                   |   8 +-
 src/input/var.c                     |   3 +
 src/text/strings.c                  |  56 +++++++++++++-
 src/video_output/vout_subpictures.c |   4 +-
 12 files changed, 344 insertions(+), 7 deletions(-)

diff --git a/include/vlc/libvlc_events.h b/include/vlc/libvlc_events.h
index f8b0e9b5b2..0e2b4d668b 100644
--- a/include/vlc/libvlc_events.h
+++ b/include/vlc/libvlc_events.h
@@ -86,6 +86,8 @@ enum libvlc_event_e {
     libvlc_MediaPlayerAudioVolume,
     libvlc_MediaPlayerAudioDevice,
     libvlc_MediaPlayerChapterChanged,
+    libvlc_MediaPlayerRecordableChanged,
+    libvlc_MediaPlayerRecordingFinished,
 
     libvlc_MediaListItemAdded=0x200,
     libvlc_MediaListWillAddItem,
@@ -283,6 +285,14 @@ typedef struct libvlc_event_t
         {
             libvlc_renderer_item_t *item;
         } renderer_discoverer_item_deleted;
+        struct
+        {
+            int new_recordable;
+        } media_player_recordable_changed;
+        struct
+        {
+            char *psz_filename;
+        } media_player_recording_finished;
     } u; /**< Type-dependent event description */
 } libvlc_event_t;
 
diff --git a/include/vlc/libvlc_media_player.h b/include/vlc/libvlc_media_player.h
index c431c235e9..1f664b86a6 100644
--- a/include/vlc/libvlc_media_player.h
+++ b/include/vlc/libvlc_media_player.h
@@ -2082,6 +2082,121 @@ LIBVLC_API int libvlc_media_player_set_role(libvlc_media_player_t *p_mi,
 
 /** @} audio */
 
+/**
+ * Can the media player record the current media?
+ *
+ * Media must be buffering or playing before it can be recorded.
+ *
+ * The media player event manager will emit a libvlc_MediaPlayerRecordableChanged event
+ * when the recordable state changes after starting media playback. The event data will
+ * describe the new recordable state, so invocation of this API method is not strictly
+ * necessary to determine when recording can be started.
+ *
+ * A libvlc_MediaPlayerRecordableChanged event will not be emitted if the media is
+ * stopped (notified by a libvlc_MediaPlayerStoppedEvent) or finishes normally (notified
+ * by a libvlc_MediaPlayerFinished event).
+ *
+ * A calling application should therefore register an event callback for those events
+ * so that it may query the new recordable state and manage recording at the appropriate
+ * time.
+ *
+ * \param p_mi media player
+ * \return true if the media player can record, false if it can not
+ * \version LibVLC 2.1.0 or later
+ */
+LIBVLC_API bool libvlc_media_player_is_recordable( libvlc_media_player_t *p_mi );
+
+/**
+ * Is the current media being recorded?
+ *
+ * \param p_mi media player
+ * \return true if recording, false if not
+ * \version LibVLC 2.1.0 or later
+ */
+LIBVLC_API bool libvlc_media_player_is_recording( libvlc_media_player_t *p_mi );
+
+/**
+ * Start recording the current media.
+ *
+ * Media must be buffering or playing before it can be recorded. A calling application
+ * can begin recording immediately on receipt of a libvlc_MediaPlayerRecordableChanged
+ * event sent via the media player event manager (if recording is possible for the
+ * currently playing media), and any time thereafter until the media stops.
+ *
+ * Media will be saved to the file path denoted by the psz_filename parameter if it is
+ * supplied. Any such supplied filename should not include a file extension as the
+ * correct file extension will automatically be appended when the file is created. This
+ * filename may denote a full path name, but each directory in the path must already
+ * exist or recording will silently fail. If the calling application chooses to specify
+ * the filename then it is the responsibility of that application to take account of
+ * this and itself make sure any needed directories are created.
+ *
+ * Alternatively, a calling application need not supply a filename and so instead let
+ * vlc automatically generate a unique filename. This will cause vlc to create a new
+ * file in the appropriate media directory for the user - for example "~/Videos". The
+ * actual filename used will be sent in an event when the recording is complete.
+ *
+ * When recording has finished and the new file has been completely saved, a
+ * libvlc_MediaPlayerRecordingFinished event will be sent via the media player event
+ * manager. The event data will contain the filename of the newly recorded file - this
+ * will either be the filename as specified by the calling application or a filename
+ * generated by vlc if the application did not supply a filename. In either case, this
+ * filename will include the automatically appended file extension.
+ *
+ * The saved media file will not be immediately available or visible until recording
+ * has completely finished and the libvlc_MediaPlayerRecordingFinished event has been
+ * received, or the media has stopped or finished normally.
+ *
+ * Recording can be stopped and started on-the-fly once the recordable state is set;
+ * each time recording is stopped and restarted a new file will be created so a calling
+ * application should take care to provide unique filenames, or defer to vlc to create
+ * unique filenames.
+ *
+ * Recording will be stopped when the media stops playing, and must be explicitly
+ * started again to restart recording, i.e. the recording state is not automatically
+ * preserved when playing media subsequently.
+ *
+ * Media player functionailty such as next/previous chapter, set time or position and
+ * so on are ineffective when recording is enabled. However, pausing the media is
+ * possible and will pause the recording; unpausing the media will resume playback and
+ * recording.
+ *
+ * Recording of the primary media or sub-items is possible.
+ *
+ * \param p_mi media player
+ * \param psz_filename name of the file to save the media to, not including any file extension,
+ *                     or NULL if vlc should generate the filename automatically
+ * \return 0 if recording was started, -1 on error
+ * \version LibVLC 2.1.0 or later
+ */
+LIBVLC_API int libvlc_media_player_record_start( libvlc_media_player_t *p_mi, const char *psz_filename );
+
+/**
+ * Stop recording the current media.
+ *
+ * This method requests that the recording stop, and will return immediately. Recording
+ * will not stop immediately.
+ *
+ * When the recording actually stops some short time later and the new file has
+ * finished being written, a libvlc_MediaPlayerRecordingFinished event will be sent via
+ * the media player event manager. The newly recorded file will not be visible or
+ * available until after this event has been sent.
+ *
+ * The event data will contain the full name of the file that was created. The filename
+ * will either be that as was specified by the calling application on invoking
+ * libvlc_media_player_record_start(), or the filename that vlc automatically generated
+ * if the calling application did not supply its own filename. In either case the
+ * filename will contain the automatically appended file extension.
+ *
+ * There is no need to invoke this method to stop the recording if the media is stopped
+ * or finishes playing normally.
+ *
+ * \param p_mi media player
+ * \return 0 if recording was stopped, -1 on error
+ * \version LibVLC 2.1.0 or later
+ */
+LIBVLC_API int libvlc_media_player_record_stop( libvlc_media_player_t *p_mi );
+
 /** @} media_player */
 
 # ifdef __cplusplus
diff --git a/lib/event.c b/lib/event.c
index f027754181..dba8a55a86 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -173,6 +173,9 @@ static const event_name_t event_list[] = {
     DEF(MediaPlayerAudioVolume)
     DEF(MediaPlayerAudioDevice)
     DEF(MediaPlayerChapterChanged)
+    DEF(MediaPlayerRecordableChanged)
+    DEF(MediaPlayerRecordingFinished)
+
 
     DEF(MediaListItemAdded)
     DEF(MediaListWillAddItem)
diff --git a/lib/libvlc.sym b/lib/libvlc.sym
index 482d95f6f1..dbcc49a7ad 100644
--- a/lib/libvlc.sym
+++ b/lib/libvlc.sym
@@ -175,6 +175,8 @@ libvlc_media_player_get_title
 libvlc_media_player_get_title_count
 libvlc_media_player_get_xwindow
 libvlc_media_player_has_vout
+libvlc_media_player_is_recordable
+libvlc_media_player_is_recording
 libvlc_media_player_is_seekable
 libvlc_media_player_is_playing
 libvlc_media_player_new
@@ -184,6 +186,8 @@ libvlc_media_player_set_pause
 libvlc_media_player_pause
 libvlc_media_player_play
 libvlc_media_player_previous_chapter
+libvlc_media_player_record_start
+libvlc_media_player_record_stop
 libvlc_media_player_release
 libvlc_media_player_retain
 libvlc_media_player_set_agl
diff --git a/lib/media_player.c b/lib/media_player.c
index fda1091cc8..43ac2d8154 100644
--- a/lib/media_player.c
+++ b/lib/media_player.c
@@ -54,6 +54,11 @@ input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
                         void * p_userdata );
 static int
+input_recordable_changed( vlc_object_t *p_this, char const *psz_cmd,
+                          vlc_value_t oldval, vlc_value_t newval,
+                          void *p_userdata );
+
+static int
 input_scrambled_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
                         void * p_userdata );
@@ -89,6 +94,10 @@ static int
 snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
 
+static int
+file_recording_finished( vlc_object_t *p_this, char const *psz_cmd,
+                         vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
 
 /*
@@ -174,6 +183,8 @@ static void release_input_thread( libvlc_media_player_t *p_mi )
                      input_seekable_changed, p_mi );
     var_DelCallback( p_input_thread, "can-pause",
                     input_pausable_changed, p_mi );
+    var_DelCallback( p_input_thread, "can-record",
+                     input_recordable_changed, p_mi );
     var_DelCallback( p_input_thread, "program-scrambled",
                     input_scrambled_changed, p_mi );
     var_DelCallback( p_input_thread, "intf-event",
@@ -271,6 +282,25 @@ input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
     return VLC_SUCCESS;
 }
 
+static int
+input_recordable_changed( vlc_object_t *p_this, char const *psz_cmd,
+                          vlc_value_t oldval, vlc_value_t newval,
+                          void *p_userdata )
+{
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(psz_cmd);
+    VLC_UNUSED(oldval);
+
+    libvlc_media_player_t *p_mi = p_userdata;
+    libvlc_event_t event;
+
+    event.type = libvlc_MediaPlayerRecordableChanged;
+    event.u.media_player_recordable_changed.new_recordable = newval.b_bool;
+
+    libvlc_event_send( &p_mi->event_manager, &event );
+    return VLC_SUCCESS;
+}
+
 static int
 input_scrambled_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
@@ -529,6 +559,23 @@ static int snapshot_was_taken(vlc_object_t *p_this, char const *psz_cmd,
     return VLC_SUCCESS;
 }
 
+static int file_recording_finished(vlc_object_t *p_this, char const *psz_cmd,
+                                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(psz_cmd);
+    VLC_UNUSED(oldval);
+
+    libvlc_media_player_t *p_mi = p_data;
+    libvlc_event_t event;
+
+    event.type = libvlc_MediaPlayerRecordingFinished;
+    event.u.media_player_recording_finished.psz_filename = newval.psz_string;
+
+    libvlc_event_send( &p_mi->event_manager, &event);
+    return VLC_SUCCESS;
+}
+
 static int corks_changed(vlc_object_t *obj, const char *name, vlc_value_t old,
                          vlc_value_t cur, void *opaque)
 {
@@ -620,6 +667,10 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     /* Input */
     var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);
     var_Create (mp, "sout", VLC_VAR_STRING);
+    char *psz = var_GetNonEmptyString( instance->p_libvlc_int, "sout" );
+    if (psz) {
+        var_SetString(mp, "sout", psz);
+    }
     var_Create (mp, "demux-filter", VLC_VAR_STRING);
 
     /* Video */
@@ -713,6 +764,8 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     var_Create (mp, "amem-set-volume", VLC_VAR_ADDRESS);
     var_Create (mp, "amem-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
     var_Create (mp, "amem-rate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
+    var_Create (mp, "recording-finished", VLC_VAR_STRING);
+    var_AddCallback (mp, "recording-finished", file_recording_finished, mp);
     var_Create (mp, "amem-channels", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
 
     /* Video Title */
@@ -807,7 +860,8 @@ static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
     /* Detach Callback from the main libvlc object */
     var_DelCallback( p_mi->obj.libvlc,
                      "snapshot-file", snapshot_was_taken, p_mi );
-
+    var_DelCallback( p_mi, "recording-finished", file_recording_finished, p_mi );
+                     
     /* Detach callback from the media player / input manager object */
     var_DelCallback( p_mi, "volume", volume_changed, NULL );
     var_DelCallback( p_mi, "mute", mute_changed, NULL );
@@ -997,6 +1051,7 @@ int libvlc_media_player_play( libvlc_media_player_t *p_mi )
 
     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
+    var_AddCallback( p_input_thread, "can-record", input_recordable_changed, p_mi );
     var_AddCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
     add_es_callbacks( p_input_thread, p_mi );
@@ -1006,6 +1061,7 @@ int libvlc_media_player_play( libvlc_media_player_t *p_mi )
         unlock_input(p_mi);
         del_es_callbacks( p_input_thread, p_mi );
         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
+        var_DelCallback( p_input_thread, "can-record", input_recordable_changed, p_mi );
         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
         var_DelCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
@@ -1932,6 +1988,62 @@ void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
     }
 }
 
+bool libvlc_media_player_is_recordable( libvlc_media_player_t *p_mi )
+{
+    input_thread_t *p_input_thread;
+    bool b_can_record;
+
+    p_input_thread = libvlc_get_input_thread( p_mi );
+    if( !p_input_thread )
+        return false;
+
+    b_can_record = var_GetBool( p_input_thread, "can-record" );
+
+    vlc_object_release( p_input_thread );
+    return b_can_record;
+}
+bool libvlc_media_player_is_recording( libvlc_media_player_t *p_mi )
+{
+    input_thread_t *p_input_thread;
+    bool b_record;
+
+    p_input_thread = libvlc_get_input_thread( p_mi );
+    if( !p_input_thread )
+        return false;
+
+    b_record = var_GetBool( p_input_thread, "record" );
+
+    vlc_object_release( p_input_thread );
+    return b_record;
+}
+int libvlc_media_player_record_start( libvlc_media_player_t *p_mi, const char* psz_filename )
+{
+    input_thread_t *p_input_thread;
+
+    p_input_thread = libvlc_get_input_thread( p_mi );
+    if( !p_input_thread )
+        return -1;
+
+    var_SetString( p_input_thread, "input-record-path", psz_filename );
+    var_SetBool( p_input_thread, "record", true );
+
+    vlc_object_release( p_input_thread );
+    return 0;
+}
+int libvlc_media_player_record_stop( libvlc_media_player_t *p_mi )
+{
+    input_thread_t *p_input_thread;
+
+    p_input_thread = libvlc_get_input_thread( p_mi );
+    if( !p_input_thread )
+        return -1;
+
+    var_SetBool( p_input_thread, "record", false );
+
+    vlc_object_release( p_input_thread );
+    return 0;
+}
+
 /**
  * Private lookup table to get subpicture alignment flag values corresponding
  * to a libvlc_position_t enumerated value.
diff --git a/modules/logger/file.c b/modules/logger/file.c
index 6b6dfd2973..80b1bb6066 100644
--- a/modules/logger/file.c
+++ b/modules/logger/file.c
@@ -58,8 +58,19 @@ static void LogText(void *opaque, int type, const vlc_log_t *meta,
     if (sys->verbosity < type)
         return;
 
+    struct timespec ts;
+    struct tm curtime;
+    char tmBuf[128];
+    timespec_get(&ts, TIME_UTC);
+    if (localtime_r(&ts.tv_sec, &curtime) == NULL) {
+        gmtime_r(&ts.tv_sec, &curtime);
+    }
+    if (strftime(tmBuf, sizeof(tmBuf), "%M%S", &curtime) == 0) {
+        strcpy(tmBuf, "no time");
+    }
+    
     flockfile(stream);
-    fprintf(stream, "%s%s: ", meta->psz_module, msg_type[type]);
+    fprintf(stream, "%s.%03d %s%s: ", tmBuf, (int)(ts.tv_nsec / 1000000), meta->psz_module, msg_type[type]);
     vfprintf(stream, format, ap);
     putc_unlocked('\n', stream);
     funlockfile(stream);
diff --git a/modules/spu/marq.c b/modules/spu/marq.c
index 516838ef63..ea7534ab74 100644
--- a/modules/spu/marq.c
+++ b/modules/spu/marq.c
@@ -287,6 +287,7 @@ static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
         }
     }
 
+    //msg_Dbg( p_filter, "mark: %s", p_sys->format );
     char *msg = vlc_strftime( p_sys->format ? p_sys->format : "" );
     if( unlikely( msg == NULL ) )
         goto out;
diff --git a/modules/stream_out/record.c b/modules/stream_out/record.c
index be10bb98c4..8f43e72261 100644
--- a/modules/stream_out/record.c
+++ b/modules/stream_out/record.c
@@ -110,6 +110,8 @@ struct sout_stream_sys_t
     int              i_id;
     sout_stream_id_sys_t **id;
     mtime_t     i_dts_start;
+    
+    char *psz_record_file;
 };
 
 static void OutputStart( sout_stream_t *p_stream );
@@ -157,6 +159,8 @@ static int Open( vlc_object_t *p_this )
     p_sys->b_drop = false;
     p_sys->i_dts_start = 0;
     TAB_INIT( p_sys->i_id, p_sys->id );
+    
+    p_sys->psz_record_file = NULL;
 
     return VLC_SUCCESS;
 }
@@ -172,6 +176,19 @@ static void Close( vlc_object_t * p_this )
     if( p_sys->p_out )
         sout_StreamChainDelete( p_sys->p_out, p_sys->p_out );
 
+    if( p_sys->psz_record_file ) {
+        for( vlc_object_t *p_mp = p_stream->obj.parent; p_mp; p_mp = p_mp->obj.parent )
+        {
+            if( var_Type( p_mp, "recording-finished" ) )
+            {
+                var_SetString( p_mp, "recording-finished", p_sys->psz_record_file );
+                break;
+            }
+        }
+
+        free( p_sys->psz_record_file );
+    }
+    
     TAB_CLEAN( p_sys->i_id, p_sys->id );
     free( p_sys->psz_prefix );
     free( p_sys );
@@ -357,7 +374,10 @@ static int OutputNew( sout_stream_t *p_stream,
     }
 
     if( psz_file && psz_extension )
+    {
+        p_sys->psz_record_file = strdup( psz_file );
         var_SetString( p_stream->obj.libvlc, "record-file", psz_file );
+    }
 
     free( psz_file );
     free( psz_output );
diff --git a/src/input/input.c b/src/input/input.c
index 04e5b3bb72..114d8bb1f0 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -3554,9 +3554,13 @@ char *input_CreateFilename(input_thread_t *input, const char *dir,
 
     filename_sanitize(filename);
 
+    //if (((ext != NULL)
+    //        ? asprintf(&path, "%s"DIR_SEP"%s.%s", dir, filename, ext)
+    //        : asprintf(&path, "%s"DIR_SEP"%s", dir, filename)) < 0)
+    //    path = NULL;
     if (((ext != NULL)
-            ? asprintf(&path, "%s"DIR_SEP"%s.%s", dir, filename, ext)
-            : asprintf(&path, "%s"DIR_SEP"%s", dir, filename)) < 0)
+            ? asprintf(&path, "%s.%s", dir, ext)
+            : asprintf(&path, "%s", dir)) < 0)
         path = NULL;
 
     free(filename);
diff --git a/src/input/var.c b/src/input/var.c
index 631b571c19..46a889d1e1 100644
--- a/src/input/var.c
+++ b/src/input/var.c
@@ -202,6 +202,9 @@ void input_ControlVarInit ( input_thread_t *p_input )
 
     var_Create( p_input, "spu-choice", VLC_VAR_INTEGER );
     var_SetInteger( p_input, "spu-choice", -1 );
+    
+    /* ES Out */
+    var_Create( p_input, "input-record-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
 
     /* Special read only objects variables for intf */
     var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
diff --git a/src/text/strings.c b/src/text/strings.c
index a15541afba..a936e5649c 100644
--- a/src/text/strings.c
+++ b/src/text/strings.c
@@ -481,7 +481,7 @@ char *vlc_b64_decode( const char *psz_src )
     return p_dst;
 }
 
-char *vlc_strftime( const char *tformat )
+char *vlc_strftime_old( const char *tformat )
 {
     time_t curtime;
     struct tm loctime;
@@ -511,6 +511,60 @@ char *vlc_strftime( const char *tformat )
     vlc_assert_unreachable ();
 }
 
+char *vlc_strftime( const char *tformat )
+{
+    struct timespec ts;
+    struct tm loctime;
+
+    if (strcmp (tformat, "") == 0)
+        return strdup (""); /* corner case w.r.t. strftime() return value */
+
+    /* Get the current time.  */
+    timespec_get(&ts, TIME_UTC);
+
+    /* Convert it to local time representation.  */
+    if (localtime_r(&ts.tv_sec, &loctime) == NULL) {
+        gmtime_r(&ts.tv_sec, &loctime);
+    }
+    size_t len = strlen(tformat);
+    char *nFormat = malloc(len + 32);
+    BOOL noMs = TRUE;
+    size_t i = 0, j = 0;
+    for (; i < len; i++, j++) {
+        if (tformat[i] == '%' && tformat[i+1] == 'l') {
+            char ms[32];
+            sprintf(ms, "%03d", (int)(ts.tv_nsec / 1000000));
+            nFormat[j] = 0;
+            strcat(nFormat, ms);
+            strcat(nFormat, &(tformat[i+2]));
+            noMs = FALSE;
+            break;
+        } else {
+            nFormat[j] = tformat[i];
+        }
+    }
+    if (noMs) nFormat[i] = 0;
+    for (size_t buflen = strlen (nFormat) + 32;; buflen += 32)
+    {
+        char *str = malloc (buflen);
+        if (str == NULL) {
+            free(nFormat);
+            return NULL;
+        }
+
+        size_t len = strftime (str, buflen, nFormat, &loctime);
+        if (len > 0)
+        {
+            char *ret = realloc (str, len + 1);
+            free(nFormat);
+            return ret ? ret : str; /* <- this cannot fail */
+        }
+        free (str);
+    }
+    free(nFormat);
+    vlc_assert_unreachable ();
+}
+
 static void write_duration(struct vlc_memstream *stream, int64_t duration)
 {
     lldiv_t d;
diff --git a/src/video_output/vout_subpictures.c b/src/video_output/vout_subpictures.c
index 7dbeee3a2a..d998fd4f70 100644
--- a/src/video_output/vout_subpictures.c
+++ b/src/video_output/vout_subpictures.c
@@ -1064,8 +1064,8 @@ static subpicture_t *SpuRenderSubpictures(spu_t *spu,
                 msg_Err(spu, "original picture size %dx%d is unsupported",
                          subpic->i_original_picture_width,
                          subpic->i_original_picture_height);
-            else
-                msg_Warn(spu, "original picture size is undefined");
+            //else
+            //    msg_Warn(spu, "original picture size is undefined");
 
             subpic->i_original_picture_width  = fmt_src->i_visible_width;
             subpic->i_original_picture_height = fmt_src->i_visible_height;
-- 
2.17.1

2022年7月27日 星期三

Build VideoLAN 3.0.17.4 on Ubuntu 18.04

參考 Build VideoLAN 3.0.16 on Ubuntu 18.04
原先想要在 Ubuntu 20.04 上建立,但發現不容易

$ cd ~/Data/VLC
$ sudo git clone https://code.videolan.org/videolan/vlc.git vlc-3.0
$ cd vlc-3.0
$ sudo git branch -a
$ sudo git log --tags --simplify-by-decoration --pretty="format:%ai %d"
$ sudo git checkout tags/3.0.17.4
$ sudo git describe --tags
// 完整地回復到最後版本,並且刪除不必要的檔案
$ sudo git reset --hard
$ sudo git clean -f -d


$ docker pull ubuntu:18.04
$ docker run --name=vlc -it -v ~/Data/VLC/vlc-3.0:/root/vlc-3.0 -v ~/Data/VLC/build_vlc:/root/build_vlc ubuntu:18.04 /bin/bash
# cd /root/vlc-3.0/
# apt-get update
# apt install software-properties-common
# add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse"
# apt-get update
# apt-cache showpkg gcc-mingw-w64-base
# apt-get install gcc-mingw-w64-base=5.3.1-8ubuntu3+17
# apt-get install gcc-mingw-w64-x86-64=5.3.1-8ubuntu3+17
# apt-get install g++-mingw-w64-x86-64=5.3.1-8ubuntu3+17
# apt-get install gcc-mingw-w64-i686=5.3.1-8ubuntu3+17
# apt-get install g++-mingw-w64-i686=5.3.1-8ubuntu3+17
# apt-get install mingw-w64-tools=5.0.3-1
# apt-get install lua5.2 libtool automake autoconf autopoint make gettext pkg-config
# apt-get install qt4-dev-tools qt5-default git subversion cmake cvs 
# apt-get install wine64-development-tools libwine-dev zip p7zip nsis bzip2
# apt-get install yasm ragel ant default-jdk protobuf-compiler dos2unix
# apt-get install subversion yasm cvs cmake ragel autopoint

以下的 HOST-TRIPLET 在 win32 時換成 i686-w64-mingw32, 在 win64 時換成 x86_64-w64-mingw32
build vlc contrib
# cd /root/vlc-3.0/
# mkdir -p contrib/win32
# cd contrib/win32
# ../bootstrap --host=HOST-TRIPLET
# make fetch
//問題
curl: (56) Recv failure: Connection reset by peer
../src/x264/rules.mak:81: recipe for target '../tarballs/x264-git.tar.bz2' failed
//解決 類似失敗,再 fetch 一次
# make fetch
# make
//問題
env: 'meson': No such file or directory
../src/fribidi/rules.mak:21: recipe for target '.fribidi' failed
make: *** [.fribidi] Error 127
//解決
# apt-get install meson
//問題
meson.build:1:0: ERROR: Meson version is 0.45.1 but project requires >= 0.48.
//解決
# apt-get install python3-pip
# python3 -m pip install meson
//問題
/root/vlc-3.0/contrib/win32/fontconfig/missing: line 81: gperf: command not found
WARNING: 'gperf' is missing on your system.
//解決
# apt-get install gperf
//問題
configure: error: BD-J requires ANT, but ant was not found. Install ant or disable jar file building (--disable-bdjava-jar)
../src/bluray/rules.mak:56: recipe for target '.bluray' failed
//解決
# apt-get install ant
//問題
[javac] /root/vlc-3.0/contrib/win32/bluray/src/libbluray/bdj/java-j2se/java/io/BDFileSystemImpl.java:21: error: BDFileSystemImpl is not abstract and does not override abstract method isInvalid(File) in FileSystem
[javac] error: Source option 5 is no longer supported. Use 6 or later.
[javac] error: Target option 1.5 is no longer supported. Use 1.6 or later.
//解決
# apt-get install openjdk-8-jdk
# update-alternatives --config java
//問題
Program nasm found: NO
meson.build:397:4: ERROR: Program 'nasm' not found or not executable
//解決
# apt-get install wget
# wget https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.gz
# tar xvf nasm-2.14.02.tar.gz
# cd nasm-2.14.02/
# ./configure
# make
# make install


build vlc
# cd /root/vlc-3.0
# ./bootstrap
//問題
ERROR: flex is not installed.
//解決
# apt-get install flex
//問題
ERROR: GNU bison is not installed.
//解決
# apt-get install bison
# mkdir win32 && cd win32
# export PKG_CONFIG_LIBDIR=/root/vlc-3.0/contrib/HOST-TRIPLET/lib/pkgconfig 
# ../extras/package/win32/configure.sh --host=HOST-TRIPLET --build=x86_64-pc-linux-gnu
# make
//問題
stream_out/chromecast/cast_channel.pb.h:17:2: error: 
#error This file was generated by an older version of protoc which is
//解決
# git clone https://github.com/protocolbuffers/protobuf.git
# cd protobuf
# git checkout tags/v3.1.0
# ./autogen.sh
# ./configure
# make
# make check
# make install
# ldconfig # refresh shared library cache.
# make package-win-common
//問題
/bin/bash: wget: command not found
Makefile:1043: recipe for target 'npapi-sdk' failed
//解決
# apt-get install wget
# make package-win32-zip


Build VideoLAN 3.0.16 on Ubuntu 18.04

參考 三 VideoLAN Compile 成熟篇

下載時要指定版本
$ cd ~/Data/VLC
$ git clone https://code.videolan.org/videolan/vlc.git vlc-3.0
$ cd vlc-3.0
$ git branch -a
$ git log --tags --simplify-by-decoration --pretty="format:%ai %d"
$ git checkout tags/3.0.16
$ git describe --tags
// 完整地回復到最後版本,並且刪除不必要的檔案
$ git reset --hard
$ git clean -f -d

利用 Docker 以利之後的重新建立
$ docker pull ubuntu:18.04
$ docker run --name=vlc -it -v ~/Data/VLC/vlc-3.0:/root/vlc-3.0 -v ~/Data/VLC/build_vlc:/root/build_vlc ubuntu:18.04 /bin/bash
# <ctrl+p><ctrl+q>
$ docker attach vlc
# <ctrl+p><ctrl+q>
$ docker stop vlc
$ docker start vlc
$ docker attach vlc
$ docker ps -a
$ docker container rm vlc
$ docker image ls
$ docker image rm <image_id>


若按照說明,不指定 mingw-w64 工具的版本,在 build 64 位元 VLC 時會產生下列問題
/usr/lib/gcc/x86_64-w64-mingw32/7.3-win32/libstdc++.a(cow-stdexcept.o):
(.text$_ZGTtNSt11logic_errorC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x36) 
relocation truncated to fit: R_X86_64_PC32 against undefined symbol `_ITM_RU8'
解決需要指定 mingw-w64 工具的版本
# cd /root/vlc-3.0/
# apt-get update
# apt install software-properties-common
# add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse"
# apt-get update
# apt-cache showpkg gcc-mingw-w64-base
# apt-get install gcc-mingw-w64-base=5.3.1-8ubuntu3+17
# apt-get install gcc-mingw-w64-x86-64=5.3.1-8ubuntu3+17
# apt-get install g++-mingw-w64-x86-64=5.3.1-8ubuntu3+17
# apt-get install gcc-mingw-w64-i686=5.3.1-8ubuntu3+17
# apt-get install g++-mingw-w64-i686=5.3.1-8ubuntu3+17
# apt-get install mingw-w64-tools=5.0.3-1


其他工具
# apt-get install lua5.2 libtool automake autoconf autopoint make gettext pkg-config
# apt-get install qt4-dev-tools qt5-default git subversion cmake cvs 
# apt-get install wine64-development-tools libwine-dev zip p7zip nsis bzip2
# apt-get install yasm ragel ant default-jdk protobuf-compiler dos2unix
# apt-get install subversion yasm cvs cmake ragel autopoint


以下的 HOST-TRIPLET 在 win32 時換成 i686-w64-mingw32, 在 win64 時換成 x86_64-w64-mingw32
build vlc contrib
# cd /root/vlc-3.0/
# mkdir -p contrib/win32
# cd contrib/win32
# ../bootstrap --host=HOST-TRIPLET
# make fetch
//問題 (改 mingw-w64 版本後,沒問題)
../bootstrap: 393: ../bootstrap: python3: not found
//解決
# apt-get install python3
# make
//問題
env: 'meson': No such file or directory
../src/fribidi/rules.mak:22: recipe for target '.fribidi' failed
make: *** [.fribidi] Error 127
//解決
# apt-get install meson
//問題
/root/vlc-3.0/contrib/win32/fontconfig/missing: line 81: gperf: command not found
WARNING: 'gperf' is missing on your system.
//解決
# apt-get install gperf
//問題
configure: error: Package requirements (fribidi >= 0.19.0) were not met:
No package 'fribidi' found
//解決
# apt-get install python3-pip
# python3 -m pip install meson
//問題
[javac] /root/vlc-3.0/contrib/win32/bluray/src/libbluray/bdj/java-j2se/java/io/BDFileSystemImpl.java:21: error: BDFileSystemImpl is not abstract and does not override abstract method isInvalid(File) in FileSystem
//解決
# apt-get install openjdk-8-jdk
# update-alternatives --config java
//問題
meson.build:388:4: ERROR: Program 'nasm' not found or not executable
//解決
# apt-get install wget
# wget https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.gz
# tar xvf nasm-2.14.02.tar.gz
# cd nasm-2.14.02/
# ./configure
# make
# make install
文件上說明 build i686-w64-mingw32 時,要移除 moc, uic, rcc,但是不能刪
# cd /root/vlc-3/contrib/i686-w64-mingw32/bin
# mv moc moc.bak
# mv uic uic.bak
# mv rcc rcc.bak
# rm -f i686-w64-mingw32/bin/moc i686-w64-mingw32/bin/uic i686-w64-mingw32/bin/rcc


build vlc
# cd /root/vlc-3.0
# ./bootstrap
//問題
../../extras/package/win32/../../../autotools/ylwrap: line 176: yacc: command not found
//解決
# apt-get install flex
# apt-get install bison
//問題
ERROR: flex is not installed.
//解決
# apt-get install flex
//問題
ERROR: GNU bison is not installed.
//解決
# apt-get install bison
# mkdir win32 && cd win32
# export PKG_CONFIG_LIBDIR=/root/vlc-3.0/contrib/HOST-TRIPLET/lib/pkgconfig 
# ../extras/package/win32/configure.sh --host=HOST-TRIPLET --build=x86_64-pc-linux-gnu
# make
//問題
stream_out/chromecast/cast_channel.pb.h:17:2: error:
#error This file was generated by an older version of protoc which is
//解決
# git clone https://github.com/protocolbuffers/protobuf.git
# cd protobuf
# git checkout tags/v3.1.0
# ./autogen.sh
# ./configure
# make
# make check
# make install
# ldconfig # refresh shared library cache.
//問題
libprotoc.so.11: cannot open shared object file: No such file or directory
//解決
# export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
//問題
/bin/bash: wget: command not found
Makefile:1043: recipe for target 'npapi-sdk' failed
make[4]: *** [npapi-sdk] Error 127
//解決
# apt-get install wget
//問題
gui/qt/main_interface.moc.cpp:22:1: error: 'QT_WARNING_DISABLE_DEPRECATED' 
does not name a type; did you mean 'QT_WARNING_DISABLE_INTEL'?
QT_WARNING_DISABLE_DEPRECATED
//解決
# qmake -v
# qtchooser -install qt5.6.3 /root/vlc-3.0/contrib/win32/qt/bin/qmake
# qtchooser -l
# export QT_SELECT=qt5.6.3
# qmake -v
# make package-win-common
# ls vlc-3.0.16/
# make package-win32-zip
# ls vlc-3.0.16-win32.zip 
//成功

2021年11月22日 星期一

gitea on QNAP (設定 Auto Start, CPU Memory 限制)

參考: http://qnap-dev.github.io/container-station-api/index.html
因為使用 QNAP 的 container station api, 不能使用 QNAP 的 2-step verification

[/share/Dockers/gitea] # cat 0_run.sh
#mkdir -p data/git
#mkdir -p data/gitea

docker run -d --name=gitea \
-p 10022:22 -p 3000:3000 \
-v /share/Dockers/gitea/data/gitea:/data/gitea \
-v /share/Dockers/gitea/data/git:/data/git \
gitea/gitea:latest

[/share/Dockers/gitea] # cat cs_get.sh
CONTAINER_ID=$(docker ps -qf "name=gitea")
echo ${CONTAINER_ID}
CONTAINER_ID=$(cd /var/lib/docker/containers && ls -d ${CONTAINER_ID}*)
echo ${CONTAINER_ID}
QIP=127.0.0.1
QPORT=8080
curl -sq -XPOST -c cookies.txt -d '{"username": "admin", "password": "password"}' http://${QIP}:${QPORT}/container-station/api/v1/login
curl -sq -XGET -b cookies.txt http://${QIP}:${QPORT}/container-station/api/v1/container/docker/${CONTAINER_ID}/all

[/share/Dockers/gitea] # cat cs_set.sh
CONTAINER_ID=$(docker ps -qf "name=gitea")
echo ${CONTAINER_ID}
CONTAINER_ID=$(cd /var/lib/docker/containers && ls -d ${CONTAINER_ID}*)
echo ${CONTAINER_ID}
QIP=127.0.0.1
QPORT=8080
curl -sq -XPOST -c cookies.txt -d '{"username": "admin", "password": "password"}' http://${QIP}:${QPORT}/container-station/api/v1/login
curl -sq -XPUT -b cookies.txt http://${QIP}:${QPORT}/container-station/api/v1/container/docker/${CONTAINER_ID}/autostart/on
curl -sq -XPOST -b cookies.txt -d \
    '{
        "cputime": 500,
        "memory": "1024m"
    }' http://${QIP}:${QPORT}/container-station/api/v1/container/docker/${CONTAINER_ID}/resource/limit

2021年11月17日 星期三

gitea on QNAP

開啟 QNAP ssh
QNAP/Control Panel/Network & File Services/Telnet / SSH
Allow SSH connection
Apply

建立 docker 使用空間
QNAP/Control Panel/Privilege/Shared Folders
Create/Shared Folder
Folder Name: Dockers
Next/Next/Finish

建立 Gitea Docker
使用 ssh 登入 QNAP
Console Management - Main menu
>> q
>> y
# cd /share/Dockers
# mkdir -p /share/Dockers/gitea
# docker pull gitea/gitea:latest
# docker run -d --name=gitea -p 10022:22 -p 3000:3000 -v /share/Dockers/gitea:/data gitea/gitea:latest

設定 Gitea 帳號
網頁登入 http://qnap_ip:3000/
底部/管理員帳戶設定
輸入 管理員帳號 密碼 電子信箱
安裝 Gitea

進入 Gitea 建立 Repository
網頁登入 http://qnap_ip:3000/
右上/+/新增儲存庫
輸入 儲存庫名稱
建立儲存庫

由 Git 匯入 Gitea
進入 git host 的目錄
> git remote add gitea http://qnap_ip:3000/UserName/Repository.git
> git push gitea --all
Username for 'http://qnap_ip:3000':
Password for 'http://UserName@qnap_ip:3000':
> git push gitea --tags
Username for 'http://qnap_ip:3000':
Password for 'http://UserName@qnap_ip:3000':

Gitea 備份 兩種方法可選
(1.進入 docker)
QNAP/Container Station/Container
按 gitea 連結,開啟 console logs
按 >_ Terminal
/bin/sh
Connect
# su - git
$ gitea dump -c /data/gitea/conf/app.ini
產生 /data/git/gitea-dump-xxxxx.zip
位於 QNAP 的 /share/Dockers/gitea/git
(2.進入 QNAP)
使用 ssh 登入 QNAP
# docker exec -u git -it -w /data/git $(docker ps -qf "name=gitea") bash -c '/app/gitea/gitea dump -c /data/gitea/conf/app.ini'
產生 /share/Dockers/gitea/git/gitea-dump-xxxxx.zip
位於 docker 的 /data/git

Gitea 還原
QNAP/Container Station/Container
按 gitea 連結,開啟 console logs
按 Stop
使用 ssh 登入 QNAP
# mkdir -p /share/Dockers/backup
# mv /share/Dockers/gitea/git/gitea-dump-xxxxx.zip /share/Dockers/backup
# cd /share/Dockers/backup
# unzip gitea-dump-xxxxx.zip
# ls /share/Dockers/backup/data/
avatars/  gitea.db   jwt/     repo-archive/
conf/     indexers/  queues/  repo-avatars/
# ls /share/Dockers/gitea/gitea/
attachments/  conf/     indexers/  log/     repo-archive/  sessions/
avatars/      gitea.db  jwt/       queues/  repo-avatars/
移除 /share/Dockers/gitea/gitea/ 內相應 /share/Dockers/backup/data/ 的資料
# mv /share/Dockers/backup/data/* /share/Dockers/gitea/gitea/
# chown -R owner.group /share/Dockers/gitea/gitea/
# rm -rf /share/Dockers/gitea/git/repositories/*
# mv /share/Dockers/backup/repos/* /share/Dockers/gitea/git/repositories/
# chown -R owner.group /share/Dockers/gitea/git/repositories/
Gitea 文件上說明,還要搬 log, database
但 log 不見了, database 使用 sqlite3, 直接是一檔案, 搬 data/gitea.db 後不須重建
QNAP/Container Station/Container
按 gitea 連結,開啟 console logs
按 Start
# rm -rf /share/Dockers/backup

Gitea Migrations 遷移
在 Gitea 1.12 以前,可以遷移 程式碼 問題 等
但在之後,無法遷移程式碼
遷移預設無法在本地端執行,需在 /data/gitea/conf/app.ini
增加下列兩行
[migrations]
ALLOW_LOCALNETWORKS = true
要遷移 Wiki 里程碑 標籤 等,需要 Access Token
網頁登入 http://qnap_ip:3000/
右上/圖標/設定/應用程式
輸入 Token 名稱
產生 Token
網頁登入 http://qnap_ip:3000/
右上/+/遷移外部儲存庫

2021年1月11日 星期一

Ubuntu 18.04 重灌

依據 https://developer.nvidia.com/deepstream-getting-started
選擇各個版本

https://docs.nvidia.com/metropolis/deepstream/dev-guide/index.html

sudo apt-get install ssh
sudo apt install python3-pip
sudo ln -s /usr/bin/python3 /usr/bin/python

安裝 NVIDIA Driver
https://www.linuxbabe.com/ubuntu/install-nvidia-driver-ubuntu-18-04
sudo lshw -c display
sudo lshw -c video
sudo ubuntu-drivers devices
sudo ubuntu-drivers autoinstall
sudo reboot
sudo lshw -c display

安裝 CUDA Toolkit
https://developer.nvidia.com/cuda-downloads
請選擇 CUDA Toolkit 10.2
Linux/x86_64/Ubuntu/18.04/deb(local)
vi ~/.bashrc
export PATH=/usr/local/cuda-10.2:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATH

安裝 CUDNN
https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html
https://developer.nvidia.com/cudnn
選擇 cuDNN v8.0.5 for CUDA 10.2
選擇 cuDNN Library for Linux (x86)
tar -xzvf cudnn-10.2-linux-x64-v8.0.5.39.tgz
sudo cp cuda/include/cudnn*.h /usr/local/cuda/include
sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*
sudo ldconfig
/sbin/ldconfig.real: /usr/local/cuda-10.2/targets/x86_64-linux/liblibcudnn.so.8 is not a symbolic link
cd /usr/local/cuda-10.2/targets/x86_64-linux/lib
sudo rm liblibcudnn.so.8 liblibcudnn.so
sudo ln -s libcudnn.so.8.0.5 libcudnn.so.8
sudo ln -s libcudnn.so.8 libcudnn.so
sudo ldconfig

安裝 TensorRT
下載 TensorRT binary
https://developer.nvidia.com/nvidia-tensorrt-7x-download
選擇 TensorRT 7.1.3.4 for Ubuntu 18.04 and CUDA 10.2 TAR package
version="7.1.3.4"
os="Ubuntu-18.04"
arch=$(uname -m)
cuda="cuda-10.2"
cudnn="cudnn8.0"
tar -xvzf TensorRT-7.1.3.4.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn8.0.tar.gz
vi ~/.bashrc
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/Data/TensorRT/TensorRT-7.1.3.4/lib
source ~/.bashrc
cd TensorRt-7.1.3.4/python
sudo pip3 install tensorrt-7.1.3.4-cp36-none-linux_x86_64.whl
cd ../uff
sudo pip3 install uff-0.6.9-py2.py3-none-any.whl
cd ../graphsurgeon/
sudo pip3 install graphsurgeon-0.4.5-py2.py3-none-any.whl

安裝 GStreamer
https://yingrenn.blogspot.com/2020/09/gstreamer.html

安裝 DeepStream
https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_Quickstart.html#dgpu-setup-for-ubuntu
sudo apt install \
 libssl1.0.0 \
 libgstreamer1.0-0 \
 gstreamer1.0-tools \
 gstreamer1.0-plugins-good \
 gstreamer1.0-plugins-bad \
 gstreamer1.0-plugins-ugly \
 gstreamer1.0-libav \
 libgstrtspserver-1.0-0 \
 libgstrtspserver-1.0-dev \
 libjansson4
git clone https://github.com/edenhill/librdkafka.git
cd librdkafka
git reset --hard 7101c2310341ab3f4675fc565f64f0967e135a6a
./configure
make
sudo make install
sudo mkdir -p /opt/nvidia/deepstream/deepstream-5.0/lib
sudo cp /usr/local/lib/librdkafka* /opt/nvidia/deepstream/deepstream-5.0/lib
下載 DeepStream
https://developer.nvidia.com/assets/Deepstream/5.0/ga/secure/deepstream_sdk_5.0.1_x86_64.tbz2
sudo tar -xvf deepstream_sdk_v5.0.1_x86_64.tbz2 -C /
cd /opt/nvidia/deepstream/deepstream-5.0/
sudo ./install.sh
sudo ldconfig

安裝 CMake v3.13
wget http://www.cmake.org/files/v3.13/cmake-3.13.5.tar.gz
tar xpvf cmake-3.13.5.tar.gz cmake-3.13.5
cd cmake-3.13.5
sudo apt-get install zlib1g-dev
sudo apt-get install curl
sudo apt-get install libcurl3
sudo apt-get install libcurl4 libcurl4-openssl-dev
./bootstrap --system-curl
make -j$(nproc)
sudo make install

安裝 Docker
https://docs.docker.com/engine/install/ubuntu/
sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

安裝 NVIDIA Docker
https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker
curl https://get.docker.com | sh \
  && sudo systemctl start docker \
  && sudo systemctl enable docker
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
   && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
   && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update
sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker
sudo groupadd docker
sudo usermod -a -G docker $USER
sudo reboot

若 sudo apt update 出現
W: Target CNF (stable/cnf/Commands-all) is configured multiple times in /etc/apt/sources.list:52 and /etc/apt/sources.list.d/docker.list:1
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4773BD5E130D1D45
sudo rm /etc/apt/sources.list.d/docker.list

安裝 TensorRT 7.1 OSS
https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html
https://github.com/NVIDIA/TensorRT/tree/master
從 master 切換到 release/7.1
下載 TensorRT OSS
TensorRT OSS: 包含 TensorRT plugins, Caffe 和 ONNX parsers 等
git clone -b master https://github.com/nvidia/TensorRT TensorRT
cd TensorRT
git submodule update --init --recursive
參考之前的 TensorRT binary
cd TensorRT-7.1.3.4
export TRT_RELEASE=`pwd`
cd $TRT_SOURCE
./docker/build.sh --file docker/ubuntu.Dockerfile --tag tensorrt-ubuntu --os 18.04 --cuda 10.2
./docker/launch.sh --tag tensorrt-ubuntu --gpus all --release $TRT_RELEASE --source $TRT_SOURCE
cd $TRT_SOURCE
mkdir -p build && cd build
cmake .. -DTRT_LIB_DIR=$TRT_RELEASE/lib -DTRT_OUT_DIR=`pwd`/out
make -j$(nproc)
exit
mkdir backup
mv $TRT_RELEASE/targets/x86_64-linux-gnu/lib/libnvinfer_plugin.so.7.1.3 backup
cp $TRT_SOURCE/build/out/libnvinfer_plugin.so.7.2.1 $TRT_RELEASE/targets/x86_64-linux-gnu/lib/libnvinfer_plugin.so.7.1.3

安裝 OpenCV 4.4
git clone https://github.com/opencv/opencv.git opencv-4.4.0 -b 4.4.0 --depth 1
git clone https://github.com/opencv/opencv_contrib.git opencv_contrib-4.4.0 -b 4.4.0 --depth 1
sudo apt-get update
sudo add-apt-repository ppa:alex-p/tesseract-ocr
sudo apt install tesseract-ocr libtesseract-dev
sudo apt-get install libleptonica-dev
sudo apt-get install qt5-default
sudo apt-get install qtcreator
sudo apt-get install build-essential cmake unzip pkg-config
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev
sudo apt-get install libgtk-3-dev
sudo apt-get install libatlas-base-dev gfortran
sudo apt-get install libhdf5-dev
sudo apt-get install python3-dev
mkdir build; cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D INSTALL_C_EXAMPLES=OFF \
-D WITH_TBB=ON \
-D BUILD_opencv_cudacodec=OFF \
-D ENABLE_FAST_MATH=1 \
-D CUDA_FAST_MATH=1 \
-D WITH_CUDA=ON \
-D WITH_CUBLAS=ON \
-D WITH_LIBV4L=ON \
-D BUILD_opencv_python3=ON \
-D BUILD_opencv_python2=OFF \
-D BUILD_opencv_java=OFF \
-D WITH_V4L=ON \
-D WITH_QT=ON \
-D WITH_OPENGL=ON \
-D WITH_GSTREAMER=ON \
-D WITH_GTK=ON \
-D OPENCV_GENERATE_PKGCONFIG=ON \
-D OPENCV_PC_FILE_NAME=opencv.pc \
-D OPENCV_ENABLE_NONFREE=OFF \
-D OPENCV_EXTRA_MODULES_PATH=/your_path_to/opencv/opencv_contrib-4.4.0/modules \
-D BUILD_EXAMPLES=ON \
-D WITH_CUDNN=ON \
-D CUDNN_VERSION="8.0.5" \
-D OPENCV_DNN_CUDA=ON \
-D CUDNN_INCLUDE_DIR=/usr/local/cuda/include \
-D CUDNN_LIBRARY=/usr/local/cuda/lib64/libcudnn.so.8.0.5 \
-D CUDA_ARCH_BIN=7.5 \
../opencv-4.4.0
#-D OPENCV_ENABLE_NONFREE=ON \
#-D OpenGL_GL_PREFERENCE=LEGACY \

make -j$(nproc)
sudo make install
sudo ldconfig
opencv_version -v

下載 Tesseract 的中文字 chi_tra.traineddata, chi_tra_vert.traineddate
https://github.com/tesseract-ocr/tessdata
放至 /usr/share/tesseract-ocr/4.00/tessdata

安裝 Xpra
https://www.xpra.org/trac/wiki/Building/Debian
sudo apt-get install libx11-dev libxtst-dev libxcomposite-dev libxdamage-dev \
 libxkbfile-dev python-all-dev
sudo apt-get install libgtk-3-dev python3-dev python3-cairo-dev python-gi-dev cython3
sudo apt-get install xauth x11-xkb-utils
sudo apt-get install libx264-dev libvpx-dev yasm
sudo apt-get install libnvidia-encode-440
sudo apt-get install libavformat-dev libavcodec-dev libswscale-dev
sudo apt-get install libturbojpeg-dev
sudo apt-get install libwebp-dev
sudo apt-get install uglifyjs brotli libjs-jquery libjs-jquery-ui gnome-backgrounds
sudo apt-get install python3-opengl python3-numpy python3-pil
sudo apt-get install python3-rencode python3-lz4 python3-dbus python3-cryptography \
 python3-netifaces python3-yaml
sudo apt-get install python3-setproctitle python3-xdg python3-pyinotify python3-opencv
sudo apt-get install libpam-dev quilt xserver-xorg-dev xutils-dev xvfb keyboard-configuration
sudo apt-get install python3-kerberos python3-gssapi
sudo apt-get install gstreamer1.0-pulseaudio gstreamer1.0-alsa \
 gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
 gstreamer1.0-plugins-ugly
sudo apt-get install cups-filters cups-common cups-pdf python3-cups
sudo apt-get install openssh-client sshpass python3-paramiko
sudo apt-get install devscripts build-essential lintian debhelper
wget https://www.xpra.org/src/xpra-4.0.4.tar.xz
tar -xf xpra-4.0.6.tar.xz
cd xpra-4.0.6
vi setup.py
#!/usr/bin/env python3
sudo ./setup.py install

安裝 frp
https://yingrenn.blogspot.com/2020/03/frp.html
vi frps.ini
[common]
bind_port = 7000
dashboard_port = 7001
dashboard_user = user
dashboard_pwd = password

vi /etc/systemd/system/frps.service
[Unit]
Description=FRP Server Daemon

[Service]
Type=simple
ExecStartPre=-/usr/sbin/setcap cap_net_bind_service=+ep /home/mark/Data/frp/frp_0.34.3_linux_amd64/frps
ExecStart=/path_to_frp/frp_0.34.3_linux_amd64/frps -c /path_to_frp/frp_0.34.3_linux_amd64/frps.ini
Restart=always
RestartSec=20s
User=nobody
PermissionsStartOnly=true
LimitNOFILE=infinity

[Install]
WantedBy=multi-user.target

sudo systemctl start frps
sudo systemctl enable ftps

安裝 Gitea Tomcat Nginx
https://yingrenn.blogspot.com/2019/11/gitea-in-ubuntu.html
https://yingrenn.blogspot.com/search/label/tomcat

安裝 x11vnc
因為 Ubuntu 18.04 改 LightDM 到 GDM3, 所以要在 console login 後,才能動作
sudo apt install x11vnc
sudo x11vnc -storepasswd
sudo chown mark.mark ~/.vnc/passwd
sudo vi /etc/systemd/system/x11vnc.service
# Description: Custom Service Unit file
# File: /etc/systemd/system/x11vnc.service
[Unit]
Description="x11vnc"
Requires=display-manager.service
After=display-manager.service

[Service]
ExecStart=/usr/bin/x11vnc -loop -nopw -xkb -repeat -noxrecord -noxfixes -noxdamage -forever -rfbport 5900 -display :1 -auth /run/user/1000/gdm/Xauthority -rfbauth /home/mark/.vnc/passwd
ExecStop=/usr/bin/killall x11vnc
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target

安裝 VirtualBox
https://www.virtualbox.org/wiki/Downloads
到官網下載
virtualbox-6.1_6.1.16-140961~Ubuntu~bionic_amd64.deb
Oracle_VM_VirtualBox_Extension_Pack-6.1.16.vbox-extpack
sudo dpkg -i virtualbox-6.1_6.1.16-140961~Ubuntu~bionic_amd64.deb
sudo groupadd win10disk
sudo usermod -a -G win10disk youruser
sudo udevadm info /dev/sdX | grep UUID
E: ID_PART_TABLE_UUID=01234567-89ab-cdef-0123-456789abcde
vi /etc/udev/rules.d/99-win10disk.rules
ENV{ID_PART_TABLE_UUID}=="1234567-89ab-cdef-0123-456789abcde", GROUP="win10disk"
ls -l /dev/sdb
brw-rw---- 1 root win10disk 8, 16 Nov 4 23:33 /dev/sdb
VBoxManage internalcommands createrawvmdk -filename .VirtualBox/Crucial1T.vmdk -rawdisk /dev/sda
sudo usermod -a -G vboxusers youruser
拷貝舊的 VM, 修改 win10.vbox, win10.vbox-prev 內的 uuid
與 .VirtualBox/Crucial1T.vmdk 的 uuid 一致
開啟 VirtualBox
File/Preferences...
Extensions 按 +
選擇剛下載的 Oracle_VM_VirtualBox_Extension_Pack-6.1.16.vbox-extpack


只有登入畫面無法使用滑鼠和鍵盤,其餘正常
sudo apt-get instll xserver-xorg-input-all

2020年5月21日 星期四

git 中文

解決 windows 中,中文亂碼問題
> git config --global gui.encoding utf-8
> git config --global i18n.commitencoding utf-8

>git remote add origin http://ip:port/username/project.git
>git push -u origin master
remote: User permission denied
fatal: unable to access 'http://ip:port/username/project.git/': The requested URL returned error: 403

修改 .git/config
[remote "origin"]
url = http://username@ip:port/username/project.git

>git push -u origin master

2020年5月14日 星期四

git stash

狀況:時常於 dev_new_fun 時,發現 master 有 bug,而 dev_new_fun 又不能 commit

$ git stash
$ git stash list
$ git checkout master
修改 master
$ git commit
$ git checkout dev_new_fun
$ git rebase master
$ git stash pop

以下為錯誤方式
$ git stash
$ git stash list
$ git checkout master
修改 master
$ git commit
$ git checkout dev_new_fun
$ git rebase master
$ git stash pop
warning: Cannot merge binary files: file.c (Updated upstream vs. Stashed changes)
出現衝突
$ git reset --hard HEAD
回復 stash pop 的上一個動作


2020年4月22日 星期三

git rebase 更改 commit 順序

git checkout master
git rebase -i <要修改的前一個 hash>
更改 pick 順序,越上面越舊,越下面越新
git push origin +master
更改遠端


2020年4月10日 星期五

git 跟上 fork 出的進度

怎麼跟上當初 fork 專案的進度?

最簡單的方式,就是砍掉重練

或是,展現你手動的技術

查詢遠端節點
$ git remote -v
一開始應該只有你 fork 出的 origin
加入 upstream
$ git remote add upstream http://192.168.0.101:8080/user1/VisualStudioTest.git
若是 url 寫錯
$ git remote set-url upstream http://192.168.0.101:8080/user/VisualStudioTest.git
$ git remote -v
此時就可查到 origin 和 upstream
下載 upstream 的更新
$ git fetch upstream
merge 本地的 master 到 upstream 的 master
$ git merge upstream/master
更新 fork 出的專案
$ git push origin master


2020年3月12日 星期四

git & VS2017

在網站上 New Repository
VisualStudioTest

> mkdir VisualStudioTest
> cd VisualStudioTest
> git init
> git remote add origin http://ip:port/username/VisualStudioTest.git
> git remote -v

編輯 .git\config 將, 避免 push 時,出現 403
[remote "origin"]
url = http://192.168.0.101:8080/user/VisualStudioTest.git
改為
[remote "origin"]
url = http://mark1@192.168.0.101:8080/user/VisualStudioTest.git

開啟 VS2017
New Project
Location: VisualStudioTest 的上層目錄
Solution name: VisualStudioTest
Create directory for solution 要勾選

在 TeamExplorer 頁籤上 可以看到 VisualStudioTest
雙按 VisualStudioTest
選 Settings/Git/Repository Settings
按 Ignore & Attributes Files/Ignore File/Add
TeamExplorer /Sync
TeamExplorer /Changes
在 Changes 的目錄下按右鍵 選 Stage
輸入 commit
按 Commit Staged
按 Synchronization/Outgoing Commits/Push 上傳至網站


2020年3月3日 星期二

git 學習筆記 5, remote and github

git clone URL
預設遠端名為 origin
只抓取 master, 其餘 branch 不下載

git remote show <origin>
git remote add <name> URL
git remote rename <a> <b>

更改 remote
git remote set-url <name> URL

git branch -a
顯示所有 branch
git remote show origin
顯示 origin 資訊

參與修改 branch_a
git checkout origin/branch_a
切換到遠端的 branch_a
git checkout -b branch_a
在此建立一個同名的 branch

pull 範例,pull 等於 fetch + rebase
git fetch origin
取得遠端 origin 的更新, origin/master 往前走了
git checkout master
git rebase origin/master
將本地的更新插入 origin/master

git push <remote> <local branch>:<remote branch>
git push <remote> :<remote branch>
刪除 remote branch

fork on github
在 github 上產生 fork (需要有 github 的帳號)
$ git clone https://github.com:user/repository.git
clone 自己在 github 上產生的 fork repository
$ git remote add upstream https://github.com:ori_user/repository.git
下載 upstream 上的所有 branch
$ git fetch upstream
切換至 develop
$ git checkout develop
若本地的 develop 比 upstream/develop 慢
$ git rebase upstream/develop
$ git checkout -b fix_branch
更新開發端成最新
更改程式
$ git add your_modify
$ git commit
$ git push origin fix_branch:fix_branch
到 github 網頁(user) Repository
進入 Pull Requests, 按下 New Pull Request
選擇 merge into: develop ... pull from: fix_branch
按 Create pull request
到 github 網頁(ori_user)
按 pull request, 找到剛才的 pull request, 點選進入
要 merge, 按 Merge Pull Request
不要 merge, 按下 Close
$ git checkout develop
$ git fetch upstream
$ git rebase upstream/develop
$ git push origin develop
$ git push origin :fix_branch
$ git branch -d fix_branch

git 學習筆記 4, branch checkout merge

checkout 移動 HEAD
git checkout hash
移動 HEAD 到 hash commit
git checkout file
將檔案還原到 HEAD 狀態,放棄修改

預設的 branch 為 master

git branch
顯示所有 branch

git branch new_branch
新建一個 branch, 但 HEAD 沒有轉移,需要自己執行下列命令
git branch new_branch hash
在 hash commit 上建立 new_branch
git checkout new_branch
可用下列命令取代上兩個命令
git checkout -b new_branch

HEAD 必須和 branch 掛勾,
如 checkout 到舊的 commit, 若要以此為基準修改,則馬上執行
git checkout -b new_branch

git branch -d del_branch
刪除 branch
但刪除前需要 checkout 到別的 branch,不能刪除目前的 branch
刪除前會判斷目前的 branch 和要刪的 branch 是否需要 merge
若需要 merge, 則會失敗, 若不 merge 要直接刪,則用
git branch -D del_branch

git checkout branch_a
git merge branch_b
Fast-forward
git merge branch_b --no-ff
no Fast-forward
若有衝突(conflict), 可查看 git status
衝突解決用 git commit
放棄 merge, 使用 git merge --abort

vi conflict_file
<<<<<<< HEAD
在 HEAD 修改的內容
=======
在 branch_b 修改的內容
>>>>>>> branch_b

git mergetool
會顯示 branch_a, base_of_a_b, branch_b
還有要 merge 的檔

git add conflict_file
git commit


2020年3月2日 星期一

git 學習筆記 3

git hash 可以只使用 6 個字元

HEAD
Branch
Tag (版本)
Remote

git log --oneline
git log -n3
git log -n3 hash

git log HEAD^^
一個 ^ 表示往前一個, 兩個^ 表示往前兩個
git log HEAD~8
~8 表示往前8個

git log --oneline file
專門針對一個 file

git log --stat file
git log --patch file

git log -S token
git log --patch -S token
有修改 token 的地方

git diff
比較 unstage 和 HEAD 的差異
git diff --staged
比較 staged 和 HEAD 的差異
git diff hash file
比較 unstage 和 hash 版本的差異
git diff hash1 hash2
比較兩次 commit 的差異

git add -p
...
Stage this hunk [y,n,q,a,d,s,e,?]?
q: 離開
a: 加入全部
y: 加入
n: 不加入
s: 分段
K: 上一段
e: 編輯
移除 -, 將 '-' 換成 ' '
移除 +, 將整行刪除

git commit --amend
隱藏舊的 commit, 用新的 commit 取代

git 學習筆記 2

git commit message 有一個 50/72 法則
第一行部要超過 50字
其餘不要超過 72字

1. Issue
2. Problem, 如何重現問題
3. Cause
4. Fix

.gitignore
參考 GitHub gitignore

Git 檔案的四種狀態
1.未追蹤(Untracked files):在版本提交後才又加進來的檔案,這些檔案並沒有被GIT所追蹤控管
2.已更改(Changes not staged for commit):已提交版本後,卻又再次修改,這些檔案會被丟回工作目錄(WD)
3.等待提交(Changes to be committed):在工作目錄(WD)的檔案執行git add後,會放在暫存區(Stage)。這些放在暫存區的檔案狀態便是等待提交囉!不論檔案是新增,修改,或刪除整個檔案,都是用 git add。
4.已提交(Committed):在暫存區(Stage)的檔案執行git commit後,檔案便置於儲存區(Repo),這些放在儲存區的檔案即是已提交的狀態。

git reset HEAD file
將 file 由 stage 返回 unstage
git reset --hard HEAD
連檔案也回復,即修改消失

git 學習筆記 1

參考書 Git - Book

mark@mark-z390-u:~/Data/git_test$ mkdir sample; cd sample
mark@mark-z390-u:~/Data/git_test/sample$ git init
Initialized empty Git repository in /home/mark/Data/git_test/sample/.git/
mark@mark-z390-u:~/Data/git_test/sample$ ls -al
total 12
drwxrwxr-x 3 mark mark 4096 Feb 29 10:06 .
drwxrwxr-x 3 mark mark 4096 Feb 29 10:05 ..
drwxrwxr-x 7 mark mark 4096 Feb 29 10:06 .git
mark@mark-z390-u:~/Data/git_test/sample$ vim README.md
mark@mark-z390-u:~/Data/git_test/sample$ git add README.md
mark@mark-z390-u:~/Data/git_test/sample$ git commit
*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'mark@mark-z390-u.(none)')
mark@mark-z390-u:~/Data/git_test/sample$ git config --global user.email "mark@mark-z390-u"
mark@mark-z390-u:~/Data/git_test/sample$ git config --global user.name "mark"
mark@mark-z390-u:~/Data/git_test/sample$ git config --global core.editor vim
mark@mark-z390-u:~/Data/git_test/sample$ git config --global merge.tool vimdiff
mark@mark-z390-u:~/Data/git_test/sample$ git config --global color.ui true
mark@mark-z390-u:~/Data/git_test/sample$ git config --global alias.st status
mark@mark-z390-u:~/Data/git_test/sample$ git st
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

new file:   README.md

mark@mark-z390-u:~/Data/git_test/sample$ 
mark@mark-z390-u:~/Data/git_test/sample$ git config --list --show-origin
file:/home/mark/.gitconfig      user.email=mark@mark-z390-u
file:/home/mark/.gitconfig      user.name=mark
file:/home/mark/.gitconfig      core.editor=vim
file:/home/mark/.gitconfig      merge.tool=vimdiff
file:/home/mark/.gitconfig      color.ui=true
file:/home/mark/.gitconfig      alias.st=status
file:.git/config        core.repositoryformatversion=0
file:.git/config        core.filemode=true
file:.git/config        core.bare=false
file:.git/config        core.logallrefupdates=true
mark@mark-z390-u:~/Data/git_test/sample$ cat ~/.gitconfig 
[user]
email = mark@mark-z390-u
name = mark
[core]
editor = vim
[merge]
tool = vimdiff
[color]
ui = true
[alias]
st = status
mark@mark-z390-u:~/Data/git_test/sample$ 

2019年11月6日 星期三

gitea in ubuntu

參考 如何在Ubuntu 18.04上安裝Gitea

安裝 Nginx
$ sudo apt update
$ sudo apt -y install nginx
安裝完成可以用瀏覽器測試 http://localhost
自動啟動 nginx
$ sudo systemctl stop nginx.service
$ sudo systemctl start nginx.service
$ sudo systemctl restart nginx.service
$ sudo systemctl reload nginx.service
$ sudo systemctl enable nginx.service

安裝 Git
$ sudo apt -y install git
$ git --version

安裝 MariaDB
$ sudo apt -y install mariadb-server mariadb-client
$ sudo systemctl stop mariadb.service
$ sudo systemctl start mariadb.service
$ sudo systemctl restart mariadb.service
$ sudo systemctl reload mariadb.service
$ sudo systemctl enable mariadb.service

建立 MariaDB root 密碼,並禁止遠端連接
$ sudo mysql_secure_installation
Enter current password for root (enter for none): Just press the Enter
Set root password? [Y/n]: Y
New password: Enter password
Re-enter new password: Repeat password
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

重新啟動 MariaDB
$ sudo systemctl restart mariadb.service

建立 gitea 資料庫, giteauser 使用者
$ sudo mysql -u root -p
CREATE DATABASE gitea;
CREATE USER 'giteauser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL ON gitea.* TO 'giteauser'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;

建立 git 使用者,以便運行 gitea
$ sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git
$ sudo mkdir -p /var/lib/gitea/{custom,data,indexers,public,log}
$ sudo chown git:git /var/lib/gitea/{data,indexers,log}
$ sudo chmod 750 /var/lib/gitea/{data,indexers,log}
$ sudo mkdir /etc/gitea
$ sudo chown root:git /etc/gitea
$ sudo chmod 770 /etc/gitea

下載並安裝 gitea
$ sudo wget -O gitea https://dl.gitea.io/gitea/1.5.0/gitea-1.5.0-linux-amd64
$ sudo chmod +x gitea
$ sudo cp gitea /usr/local/bin/gitea
$ sudo vi /etc/systemd/system/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
#After=mysqld.service
#After=postgresql.service
#After=memcached.service
#After=redis.service

[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
# If you want to bind Gitea to a port below 1024 uncomment
# the two values below
###
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

$ sudo systemctl daemon-reload
$ sudo systemctl enable gitea
$ sudo systemctl start gitea
$ sudo system status gitea

使用 Nginx 代理 gitea
$ sudo vi /etc/nginx/sites-available/git
upstream gitea {
 server 127.0.0.1:3000;
}

server {
 listen 80 default_server;
 listen [::]:80 default_server;
 server_name example.com;
 root /var/lib/gitea/public;
 access_log off;
 error_log off;

 location / {
 try_files maintain.html $uri $uri/index.html @node;
 }

 location @node {
 client_max_body_size 0;
 proxy_pass http://localhost:3000;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header Host $http_host;
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_max_temp_file_size 0;
 proxy_redirect off;
 proxy_read_timeout 120;
 }
}

$ sudo ln -s /etc/nginx/sites-available/git /etc/nginx/sites-enabled
$ sudo systemctl reload nginx.service

開啟網頁設定 Gitea
http://localhost/install


錯誤排除

最後安裝動作 http://localhost:3000 出現下列錯誤
Index column size too large. The maximum column size is 767 bytes

sudo mysql -u root -p
[sudo] password for user: (os 的密碼)
Enter password:(mysql 的密碼)
MariaDB [(none)]> show global variables like 'innodb_large%';
MariaDB [(none)]> set global innodb_large_prefix = ON;
MariaDB [(none)]> set global innodb_default_row_format=dynamic;

MariaDB [(none)]> select user, host from mysql.user;
MariaDB [(none)]> show grants for 'giteauser'@'localhost';
MariaDB [(none)]> revoke all privileges, grant option from 'giteauser'@'localhost';
MariaDB [(none)]> drop user 'giteauser'@'localhost';
MariaDB [(none)]> drop database gitea;

http://localhost:3000/install
只能執行一次,下一次就回 404
sudo vi /etc/gitea/app.ini
INSTALL_LOCK=false
sudo systemctl stop gitea
sudo systemctl start gitea

http://localhost:3000/install
主要填寫 Database
Username=giteauser
Password=
點開 Server and Third-Party Service Settings
Disable Self-Registration
Require Sign-In to View Pages
點開 Administrator Account Settings
建立第一個 gitea 管理者