1. 程式人生 > >HLS學習(七)HLSDownloader原始碼分析(6)下載TS檔案片段

HLS學習(七)HLSDownloader原始碼分析(6)下載TS檔案片段

下載TS檔案片段

處理完Master PlayList和Media PlayList之後就可以開始下載TS視訊片段了
下載的流程如下:
1、初始化本地的檔名
2、設定本地檔案的訪問許可權
3、建立任務佇列
4、建立PlayList更新執行緒,因為伺服器上的m3u8檔案可能會經過改動,因此需要實時更新
5、遍歷任務佇列,佇列裡面存放的是TS媒體檔案的資訊,根據這些資訊,下載TS視訊檔案。另外,如果下載的檔案的大小已經超過了最大的檔案的大小,那麼表示伺服器上的m3u8檔案已經修改,需要更新PlayList
// 下載TS檔案
int download_hls(struct hls_media_playlist *me,bool live_streamming,int max_file_size,const char* custom_filename, std::string user_agent, bool force_overwrite=true)
{
    if(live_streamming){
        MSG_VERBOSE("Downloading Live Streaming\n");
    }else{
        MSG_VERBOSE("Downloading %d segments.\n", me->count);
    }

    char filename[MAX_FILENAME_LEN];

	// 初始化本地檔名
    if (custom_filename!=NULL && *custom_filename!='\0') {
        strcpy(filename, custom_filename);
    } else {
        strcpy(filename, "000_hls_output.ts");
    }

	// 訪問許可權的設定
    if (access(filename, F_OK) != -1) {
        if (force_overwrite) {
            if (remove(filename) != 0) {
                MSG_ERROR("Error overwriting file");
                exit(1);
            }
        } else {
            char userchoice;
            MSG_PRINT("File already exists. Overwrite? (y/n) ");
            scanf("\n%c", &userchoice);
            if (userchoice == 'y') {
                if (remove(filename) != 0) {
                    MSG_ERROR("Error overwriting file");
                    exit(1);
                }
            } else {
                MSG_WARNING("Choose a different filename. Exiting.\n");
                exit(0);
            }
        }
    }

	// 建立任務佇列
    std::shared_ptr<Queue<std::shared_ptr<hls_media_segment>>> segment_queue(new Queue<std::shared_ptr<hls_media_segment>>());
    std::mutex mutex_;
    std::condition_variable cond_;
    bool flag_finish=false;

    int lattest_sequence_number=-1;
	
	// 更新PlayList的執行緒,伺服器上的m3u8檔案可能更新得很頻繁
    std::shared_ptr<std::thread> update_playlist_thread=std::make_shared<std::thread>([&]{    
        bool first_download_iteration = true;
        int wait_second= me->target_duration > 0 ?me->target_duration:1;
        while(first_download_iteration || live_streamming){
            {
                std::lock_guard<std::mutex> mlock(mutex_);
                if(flag_finish){
                    break;
                }
            }

            first_download_iteration = false;
            int i=0,current_file_count=0;
            
            for (; i < me->count&&me->media_segment[i].sequence_number <= lattest_sequence_number; i++);
            
            //Build downloading queue
            current_file_count = i;
            for (; i < me->count; i++) {
                //MSG_PRINT(me->media_segment[i].url); 
                segment_queue->push(std::make_shared<hls_media_segment>(me->media_segment[i]));
                lattest_sequence_number= me->media_segment[i].sequence_number;
            }


            MSG_PRINT("Update %d ts to queue\n",me->count - current_file_count);
            {
                std::unique_lock<std::mutex> mlock(mutex_);
                cond_.notify_one();
            }
            if(live_streamming){
                if(me->count - current_file_count < 1){    
                    
                    wait_second = wait_second*2;
                }else{
                    wait_second = me->target_duration;
                }
                MSG_PRINT("Sleep %d Seconds\n",wait_second);
                std::this_thread::sleep_for(std::chrono::seconds(wait_second));
                

                if (handle_hls_media_playlist(me,user_agent)) {
                    break;
                }
                MSG_PRINT("Reload m3u3 Playlist\n");
            }
        }
    });


    FILE *pFile = fopen(filename, "wb");
    int file_size=0;
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        cond_.wait(mlock,[segment_queue]()->bool{return !(segment_queue->empty());});
    }
    while(!segment_queue->empty()){
        auto segment=segment_queue->pop();

        MSG_PRINT("Thread %d Downloading segment %d\n", 0, segment->sequence_number);
        auto buffer = std::make_shared<ByteBuffer>();

		// 下載TS視訊片段
        buffer->len = (int)get_data_from_url(segment->url.c_str(), NULL, &(buffer->data), BINARY,user_agent);
       
        if (me->encryption == true && me->encryptiontype == ENC_AES128) {
            decrypt_aes128(segment.get(), buffer.get());
        } else if (me->encryption == true && me->encryptiontype == ENC_AES_SAMPLE) {
            decrypt_sample_aes(segment.get(), buffer.get());
        }
		
		// 寫入本地檔案中
        fwrite(buffer->data, 1, buffer->len, pFile);
        fflush(pFile);
        free(buffer->data);
        file_size=file_size+buffer->len;
        MSG_PRINT("Totally Downloading Size: %d....Max size %d\n", file_size,max_file_size);
		
		// 如果下載的檔案的大小已經超過了最大的檔案大小,那麼表示伺服器上的PlayList可能已經更新了,本地應該及時更新
        if(max_file_size>0 && file_size >= max_file_size){
            std::lock_guard<std::mutex> mlock(mutex_);
            flag_finish = true; // 設定為true,那麼更新執行緒就可以繼續執行了
            MSG_PRINT("Reach Max Size %d! Finish.\n", max_file_size);
            break;
        }
        //delete segment;
    }


    fclose(pFile);
    update_playlist_thread->join();
    
    return 0;
}


相關推薦

HLS學習HLSDownloader原始碼分析6下載TS檔案片段

下載TS檔案片段 處理完Master PlayList和Media PlayList之後就可以開始下載TS視訊片段了 下載的流程如下: 1、初始化本地的檔名 2、設定本地檔案的訪問許可權 3、建立任務佇列 4、建立PlayList更新執行緒,因為伺服器上的m3u8檔案

HLS學習HLSDownloader原始碼分析5解析Media PlayList

解析Media PlayList     PlayList就是m3u8檔案或者索引檔案,Media PlayList也叫媒體播放列表或者媒體索引檔案解析Media PlayList的流程如下:1、如果hls_media_playlist結構體中媒體資訊存在,那麼先刪除2

HLS學習HLSDownloader原始碼分析3總體流程

總體流程 下載一個媒體檔案的流程 1、初始化 2、根據URL下載m3u8檔案(即PlayList檔案) 3、判斷m3u8檔案的型別,是Master PlayList還是Media PlayList 4、如果是Master PlayList,那麼分析這個PlayList

HLS學習HLSDownloader原始碼分析4解析Master PlayList

解析Master PlayList     PlayList就是m3u8檔案或者索引檔案,Master PlayList也叫一級索引檔案。 解析Master PlayList的過程如下: 1、

opendaylightLi l2switch 原始碼分析2--parent

本文主要介紹l2switch中的parent工程,該工程定義了執行L2switch所使用的依賴模組以及版本等。 該工程下只有一個pom.xml檔案,下面對該檔案中的主要內容進行說明: 1. <parent>     <groupId>org.open

Flume NG原始碼分析ChannelSelector

前幾篇介紹了Flume NG Source元件的基本情況,接下來看看Channel相關的元件,Channel相關元件有: 1. Channel 2. ChannelSelector 3. Interceptor / InterceptorChain 4. ChannelProcess

OpenCV學習筆記31KAZE 演算法原理與原始碼分析KAZE的原始碼優化及與SIFT的比較

  KAZE系列筆記: 1.  OpenCV學習筆記(27)KAZE 演算法原理與原始碼分析(一)非線性擴散濾波 2.  OpenCV學習筆記(28)KAZE 演算法原理與原始碼分析(二)非線性尺度空間構建 3.  Op

OpenCV學習筆記30KAZE 演算法原理與原始碼分析KAZE特徵的效能分析與比較

      KAZE系列筆記: 1.  OpenCV學習筆記(27)KAZE 演算法原理與原始碼分析(一)非線性擴散濾波 2.  OpenCV學習筆記(28)KAZE 演算法原理與原始碼分析(二)非線性尺度空間構

co_routine.cpp/.h/inner.h上 : 協程的建立—— libco原始碼分析學習筆記

由於本原始碼蠻長的,所以按照功能劃分模組來分析。 一、協程(task)的建立 參考部落格 struct stCoRoutine_t為協程環境變數型別(主要是物理環境,比如上下文)。儲存著執行時協程執行環境所有資訊。 程式碼段 小部件 struct s

co_routine.cpp/.h/inner.h第三部分 : 協程的執行—— libco原始碼分析學習筆記

由於本原始碼蠻長的,所以按照功能劃分模組來分析,分為若干部分,詳見二級目錄↑ 三、協程的執行 void co_yield_env( stCoRoutineEnv_t *env );//將當前執行的env從協程棧中出棧並將執行權交給父協程。 void co_y

co_routine.cpp/.h/inner.h第四部分:定時器和事件迴圈—— libco原始碼分析學習筆記

由於本原始碼蠻長的,所以按照功能劃分模組來分析,分為若干部分,詳見二級目錄↑ 定時器和事件迴圈 libco管理定時事件便是使用時間輪這種資料結構 定時器前驅知識本篇只稍微提一下,具體知識請參考《Linux高效能伺服器程式設計 遊雙 著》第11章,或其他方式學

Redis原始碼分析--- multi事務操作

        redis作為一非關係型資料庫,竟然同樣擁有與RDBMS的事務操作,不免讓我覺得比較驚訝。在redis就專門有檔案就是執行事務的相關操作的。也可以讓我們領略一下,在Redis的程式碼中是如何實現事務操作。首先亮出mulic.c下面的一些API。 /* ===

mochiweb原始碼分析

前面說到了filelib:is_dir這函式,如果是目錄,則執行true流程,進入mochiweb_request:maybe_redirect/4這函式 然後根據第一個引數是否是[],呼叫不同的分支 回到之前說的true、false判斷,如果是檔案,則呼叫mochiweb_reque

Go學習之go-ethereum【以太坊】原始碼分析

關於Go語言環境的安裝與配置,我在《入門篇》進行了詳細講解,有需要的朋友可以前往閱讀,本文進入當下比較火熱的區塊鏈專案 - 以太坊(go-ethereum)進行原始碼解讀。本文內容純屬個人見解,有錯誤理解或者不足之處還請見諒,歡迎一起交流學習。    - 環境準備    -

TensorFlow學習筆記之原始碼分析3---- retrain.py

"""簡單呼叫Inception V3架構模型的學習在tensorboard顯示了摘要。 這個例子展示瞭如何採取一個Inception V3架構模型訓練ImageNet影象和訓練新的頂層,可以識別其他類的影象。 每個影象裡,頂層接收作為輸入的一個2048維向量。這

Java併發包原始碼學習之執行緒池ThreadPoolExecutor原始碼分析

Java中使用執行緒池技術一般都是使用Executors這個工廠類,它提供了非常簡單方法來建立各種型別的執行緒池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService

Java併發包原始碼學習之AQS框架AbstractQueuedSynchronizer原始碼分析

經過前面幾篇文章的鋪墊,今天我們終於要看看AQS的廬山真面目了,建議第一次看AbstractQueuedSynchronizer 類原始碼的朋友可以先看下我前面幾篇文章: 分析原始碼是非常枯燥乏味的一件事,其實程式碼本身其實就是最好的說明了,因此基本都是貼出一些程式碼加上一些註釋, 因為Abstract

Libevent原始碼分析--- IOCP

event_base中有一個iocp變數,event_base的初始化函式中會呼叫event_base_start_iocp開啟iocp功能,event_base_start_iocp又會呼叫event_iocp_port_launch來初始化IOCP:

Ceph 學習——OSD讀寫流程與原始碼分析

直接上圖: 同樣當前最新的版本,和之前的版本有所不同,有一些模組簡化了,類的名字也改了。先介紹圖中涉及的相關的類,然後在對類中具體函式主要呼叫流程進行分析。 OSD 模組主要的類 盜圖:其中ReplicatedPG 在最新的版本中

.linux開發之uboot移植——uboot原始碼分析2-啟動第二階段之start_armboot函式分析1

一.uboot啟動第二階段之start_armboot函式簡介 1.start_armboot函式簡介 (1)這個函式在uboot/lib_arm/board.c的第444行開始到908行結束。 (2)、即一個函式組成uboot第二階段 2、