2023年10月25日星期三

GStreamer debug 的方式 - 第一次開機執行GStreamer command  會慢1~2秒

 這邊先記錄debug的方式

  我們可以在Linux作業系統底下,使用這個command

  =>  export GST_DEBUG="*:6"

實驗1:

我們使用gstreamer的debug mode發現幾件事情

1. 執行 get-lauch-1.0 之後,會在檔案系統中產生名為 “registry.aarch64.bin”的檔案

2. 這個cache檔案存放在 /home/root/.cache/gstreamer-1.0/

接著我們寫了簡單的script,會去執行這個command

=> /usr/bin/gst-launch-1.0 filesrc location=/etc/Track.awb ! decodebin ! audio convert ! audio resample ! audio/x-raw,rate=48000,channels=2,format=S16LE ! volume volume=2 ! alsasink device="hw:0,0"

接著我們計算有cache檔案的執行時間,去比較沒有cache檔案的執行時間

=> 有cache: 0.2 sec    沒有cache: 1 sec  (計算時間方式,使用teraterm的timestamp 去計算)







實驗2:

事先儲存好這個cache檔,重新更新這個device的韌體,刷新後把cache檔案放到 /home/root/.cache/gstreamer-1.0/

結果會跟實驗1當中,有cache的結果一樣,約執行0.2秒


2023年7月9日星期日

Sharp LC-40SF466T 電視,開不了機,綠燈恆亮,可以試試看這個方法

 今天電視突然無預警的沒有畫面,關閉電源再度開啟後,電視綠燈恆亮

 按電源按鈕開啟電視,都沒有反應,這台電視才使用4年,想說會不會是主機板的電源管理部分出了問題,於是上網找了一些資訊

  這台電視,只有一個主機板 + 邏輯版,主機板的部分,是三合一板 (),把電源板與主機板合在一起,然後找到下面方法,試了之後,還真的讓我的螢幕亮了起來

  接著按下電源按鈕之後,就可以讓電視回到紅燈狀態,資訊與步驟如下:


夏普液晶電視的很多機型主板有5次記憶保護,當背光或者電源發生故障時,只要開機5次以上,就形成保護記憶,解除方法步驟如下

Step1: 按住 信號源鍵(INPUT/AV/TV) 音量減鍵,同時拔掉電源插頭

Step2: 之前按住的鍵,到這步驟都不要放開,再度插上電源插頭


我完成以上兩個步驟後,電視就恢復正常了~ 不用花大錢了 :D

2023/11/09 更新最新狀況,後來還是遇到綠燈恆亮問題,上蝦皮買了一塊二手主機板 $1200
自行拆開更換後,正常服役中~

2023年7月3日星期一

[Linux Audio] 使用ALSA API 實作amixer command: amixer -c0 set i2s_clock on,Bug 修正

 之前有寫了一篇 ALSA API 實作amixer command:  amixer -c0 set i2s_clock on

 後來發現,有兩個 Bug 需要被修正,分別如下:

 1. 第一次如果使用 i2s_clock off,會造成 kernel 端出現 core_dump 錯誤,導致執行時間會變很長 (原本大概只需要 20ms,結果時間拉長到 200ms)

 2. 發現這個上層的API程式,呼叫Linux Kernel 層,去triger I2S,出現 non-blocking的現象(application 呼叫底層程式,但沒有等待底層做完,就直接繼續執行application層的事情)


因此加入底下一些程式,來解決這兩個問題 (用不同顏色標出)

#include <stdio.h>

#include <alsa/asoundlib.h>


int set_mixer_onoff(const char *control_name,
                            int option)
{
    const char *device = "hw:0";  // 音訊設備的名稱或識別符號
    //const char *control_name = "i2s_clock";  // 控制元素的名稱, 由API參數帶入

    snd_ctl_t *handle;
    snd_ctl_elem_id_t *elem_id;
    snd_ctl_elem_value_t *elem_value;
    int err;
    int ori_value;
    int i2s_is_set = 0;

    // 打開音訊設備的控制界面
    err = snd_ctl_open(&handle, device, 0);
    if (err < 0) {
        printf(":Cannot open device interface: %s\n", snd_strerror(err));  //無法打開音訊設備的控制界面
        return err;
    }


    // 分配並初始化 snd_ctl_elem_id_t 結構
    snd_ctl_elem_id_alloca(&elem_id);
    snd_ctl_elem_id_set_name(elem_id, control_name);


    // 獲取控制元素的索引
    snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);

    // 獲取控制元素的數值
    snd_ctl_elem_value_alloca(&elem_value);
    snd_ctl_elem_value_set_id(elem_value, elem_id);
    snd_ctl_elem_value_set_boolean(elem_value, 0, option);  // 設置boolean值,由API參數option帶入

     

    // This is for fixing the core_dump issue
    // 先取得之前狀態,如果跟這次要設定的狀態相同,就不需要再做,直接 return.
    err = snd_ctl_elem_read(handle, elem_value);

    if (err < 0) {

            printf("Cannot read value: %s\n", snd_strerror(err));

            snd_ctl_close(handle);

            return err;

    }


    ori_value = snd_ctl_elem_value_get_boolean(elem_value, 0);

    if( option == ori_value )

    {

        printf("No need to Set, option equal to ori_value\n");

        snd_ctl_close(handle);

        return 0;

    }

    else

        snd_ctl_elem_value_set_boolean(elem_value, 0, option); //設定boolean值,由API參數option帶入

    /////////////////////////////////////////////


    // 設置控制元素的數值
    err = snd_ctl_elem_write(handle, elem_value);
    if (err < 0) {
        printf("Cannot set value: %s\n", snd_strerror(err));  //無法設置控制元素的數值
        snd_ctl_close(handle);
        return err;

    }

    // Poll the control value until it reaches the desired state

    // For fix the non-blocking issue

    while (!i2s_is_set) {

        // Sleep for a short period to avoid busy-waiting

        usleep(5000); // Sleep for 5 milliseconds


        // Read the control value

        err = snd_ctl_elem_read(handle, elem_value);

        if (err < 0) {

            printf("Cannot read value 2th: %s\n", snd_strerror(err));

            snd_ctl_close(handle);

            return err;

        }


        // Check if the control value is equal to the desired state

        int cur_value = snd_ctl_elem_value_get_boolean(elem_value, 0);

        if (cur_value == option) {

            i2s_is_set = 1;

        }

    }

    // 關閉音訊設備的控制界面
    snd_ctl_close(handle);
    printf("Setting %s %d success\n", control_name, option);  //已成功設置 i2s_clock 為 on
    return 0;

}


在Linux當中,應用程式,計算執行時間的範例程式

這個範例,計算單位為 microSecond(10-6)

#include <time.h>

 


clock_t start, end;

double cpu_time_used;

 

 

printf("** Start to measure time **\n");

start = clock();

...

...

...

end = clock();


cpu_time_used = ((double) (end - start))*1000000 / (CLOCKS_PER_SEC);

printf("** execution time:%f microseconds\n", cpu_time_used);

printf("** End of measure time **\n");


Linux kernel 4.19版 中,計算執行時間的方法,範例程式

首先需要 include 這個 header file 

#include <linux/time.h>


然後使用下面的方式,搭配 printk 在kernel 端印出計算時間,
這邊我們取 microseconds (10-6)

struct timeval start_time, end_time;

unsigned long elapsed_time;


 printk(KERN_ERR "#### measure time start %s ####\n", __func__);

do_gettimeofday(&start_time);   // Get the starting time

...

...

...

do_gettimeofday(&end_time);     // Get the ending time

 

// Calculate the executing time

elapsed_time = (end_time.tv_sec - start_time.tv_sec) * 1000000 +

                              (end_time.tv_usec - start_time.tv_usec);


printk(KERN_ERR "#### Elapsed time: %lu microseconds\n", elapsed_time);

printk(KERN_ERR "#### measure time end %s ####\n", __func__);



另外在 kernel 較新的版本當中,如果 do_gettimeofday() 無法使用,可以試試看下列方法

#include <linux/ktime.h>
#include <linux/types.h>

ktime_t start, end;
s64 elapsed_ns, 
elapsed_us;

start = ktime_get();

...
...
...

end = ktime_get();

elapsed_ns = ktime_to_ns(ktime_sub(end, start));
elapsed_us = ktime_to_us(elapsed_ns );

printk(KERN_ERR "#### Elapsed time: %lld microseconds\n", elapsed_us);


2023年6月6日星期二

Android 4.2.2 無法使用 YouTube的解決方法,使用 NewPipe Legacy

 在網路上查詢了很久,總算找到了一個解法
 並且讓我的舊平板電腦,也可以上 YouTube看影片

NewPipe 是一個 Open source,如果你的Android 是 4.2.2 (低於 Android 4.4)
可以到以下的網站,下載 NewPipe Legacy (0.20.8版)

https://f-droid.org/packages/org.schabi.newpipelegacy/#latest












If your device is Android 4.2.2 and cannot successfully use the YouTube, you can install the "NewPipe Legacy" APK.

The NewPipe Legacy is an open source. After I install into my Table computer, the YouTube can work now. You can use your Table computer and click the following link. Then download the version 0.20.8, this should be work. 

Best Regards!


[Linux Audio] 使用ALSA API 實作amixer command: amixer -c0 set i2s_clock on

當在我們的device端,可以使用命令 "amixer -c0 set i2s_clock on" 來開啟 i2s_clock
                                   也可以使用命令 "amixer -c0 set i2s_clock off" 來關閉 i2s_clock

這表示我們的device 底層,已經有建立 "i2s_clock" 這個kcontrol,因此我們使用amixer 命令,才可以順利的再取呼叫到kcontrol所定義好的 function,進而做到function裡面所實作好的行為

簡單的kcontrol,大概如下圖所示: (此範例為MTK mt6880 audio card)





*註 SOC_SINGLE_EXT 是Linux底下的巨集,想知道定義可以去kernel底下 include/sound/soc.h 查看

以上簡單描述一下kcontrol,接下來進入本文章的主題,如何使用ALSA API來實作一個API,讓別人呼叫此API,就達到跟 amixer command 一樣的行為,底下是一個簡單的實作範例


#include <stdio.h>

#include <alsa/asoundlib.h>


int set_mixer_onoff(const char *control_name,
                            int option)
{
    const char *device = "hw:0";  // 音訊設備的名稱或識別符號
    //const char *control_name = "i2s_clock";  // 控制元素的名稱, 由API參數帶入

    snd_ctl_t *handle;
    snd_ctl_elem_id_t *elem_id;
    snd_ctl_elem_value_t *elem_value;
    int err;

    // 打開音訊設備的控制界面
    err = snd_ctl_open(&handle, device, 0);
    if (err < 0) {
        printf(":Cannot open device interface: %s\n", snd_strerror(err));  //無法打開音訊設備的控制界面
        return err;
    }


    // 分配並初始化 snd_ctl_elem_id_t 結構
    snd_ctl_elem_id_alloca(&elem_id);
    snd_ctl_elem_id_set_name(elem_id, control_name);


    // 獲取控制元素的索引
    snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);

    // 獲取控制元素的數值
    snd_ctl_elem_value_alloca(&elem_value);
    snd_ctl_elem_value_set_id(elem_value, elem_id);
    snd_ctl_elem_value_set_boolean(elem_value, 0, option);  // 設置boolean值為1,由API參數帶入


    // 設置控制元素的數值
    err = snd_ctl_elem_write(handle, elem_value);
    if (err < 0) {
        printf("Cannot set value: %s\n", snd_strerror(err));  //無法設置控制元素的數值
        snd_ctl_close(handle);
        return err;

    }


    // 關閉音訊設備的控制界面
    snd_ctl_close(handle);
    printf("Setting %s %d success\n", control_name, option);  //已成功設置 i2s_clock 為 on
    return 0;

}


最後,編譯成為Library之後,提供對方 library 以及 xxx.h 裡面有包含此API宣告
int set_mixer_onoff(const char *control_name, int option);

對方就可以使用了!



使用 lsblk 印出 emmc 每個 partition的"名字"與"size"

使用以下的command可以印出 eMMC的 partition資訊 lsblk --bytes --output name,partlabel,size   參數說明 --bytes: partition的大小,以byte的方式輸出 --output: 後面可以指定要輸出的內容...