天天看點

ios 播放ts流視訊思路

我們知道FFmpeg能夠處理本地檔案或者網絡檔案

隻要傳入url給avformat_open_input函數就可以了

實際上對于音視訊的進行中,

很少會給一個完整有效的網址

很多時候都是傳遞流資訊過來

比如h264 和aac的裸流,這些流隻需要調用

ffmpeg 的解碼函數即可。

但對于封裝格式來說,如何把資料傳遞給ffmpeg就成了一個很大的問題

我參考雷曉華的記憶體讀取,知道了ffmpeg 提供了某種回調函數

來讓外部傳入流資料,

int size = 188000;
    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();
    unsigned char *aviobuffer=(unsigned char *)av_malloc(size);
    AVIOContext *avio =avio_alloc_context(aviobuffer, size,0,NULL,read_buffer,NULL,NULL);
    pFormatCtx->pb=avio;
    int ret = avformat_open_input(&pFormatCtx,NULL,NULL,NULL);
           

注意這裡面是沒有任何地方傳遞url 的,read_buffer才是ffmpeg讀取音視訊資料的

地方

int read_buffer(void *opaque, uint8_t *buf, int buf_size){
    int cpSize = 0;
    YYInputQueue *inputQueue = (YYInputQueue *)[(TSDeMuxer *)object getInputQueue];
    while (![(TSDeMuxer *)object stop]) {
        if (inputQueue->getQueueSize()>0) {
            InputStreamData streamData;
            inputQueue->getpacket(streamData);
            memcpy((uint8_t *)buf,streamData.data, streamData.size);
            buf_size = (int )streamData.size;
            printf("input read size=[%d] data=%s\n",buf_size,buf);
            cpSize = buf_size;
            break;
        } else {
            usleep(40*1000);
            continue;
        }
        
    }
    return cpSize;
}
           

這是從隊列裡面取出資料,如果資料沒有就繼續等下去,一直等到資料有為止,

- (void)sendInputDataThread {
    //int perSize = 32768;
    int perSize = 188000;
    FILE *fp_open1=NULL;
    NSFileManager* manager = [NSFileManager defaultManager];
    NSString  *filepath = [[NSBundle mainBundle] pathForResource:@"sdcard" ofType:@"ts"];
    long long size = [[manager attributesOfItemAtPath:filepath error:nil] fileSize];
    int count = (int ) size/perSize;
    fp_open1 = fopen([filepath UTF8String], "r");
    for (int i=0;i<=count;i++) {
        if(!feof(fp_open1)){
            if (self.demuxer.stop) {
                break;
            }
            uint8_t *buf=(uint8_t *)malloc(perSize);;
            int buf_size = perSize;
            if (i== count) {
                buf_size =  size - perSize*count;
            }
            int true_size=fread(buf,1,buf_size,fp_open1);
            if (true_size >0) {
                printf("input write size=[%d] data=%s\n",true_size,buf);
                [self.demuxer sendData:(char *)buf andSize:(int)buf_size andTimeStamp:0];
            }
            usleep(2000*1000);
        }
    }
    fclose(fp_open1);
}
           

這裡是發送資料的代碼,可以用來模拟從本地讀取檔案,

然後切成一片一片的片段,每次讀取一段後睡眠一段時間(模拟網絡情況)

然後将片段送入隊列

readpacket 函數會在隊列size大于0 的情況下就傳回結果了

剩下的代碼就不用寫了,和平常的編解碼一樣了,