1、音視頻解封裝流程---解復用

對于一個視頻文件(mp4格式/flv格式),audio_pkt或者video_pkt是其最基本的數據單元,即視頻文件是由獨立的視頻編碼包或者音頻編碼包組成的。
解復用就是從視頻文件中把視頻包/音頻包單獨讀取出來保存成獨立文件,那么如何得知packet是視頻包還是音頻包呢?有這樣一個結構體:

typedef struct AVPacket {AVBufferRef *buf;      // 指向數據緩沖區的指針int64_t pts;           // 顯示時間戳int64_t dts;           // 解碼時間戳uint8_t *data;         // 指向數據緩沖區的指針int size;              // 數據緩沖區大小int stream_index;      // 數據包所屬的流標簽int flags;             // 數據包的標志位AVPacketSideData *side_data; // 側數據數組int side_data_elems;   // 側數據數組的元素數量int64_t duration;      // 數據包的持續時間int64_t pos;           // 數據包在輸入文件中的位置int64_t convergence_duration; // 數據包的收斂持續時間(棄用)
} AVPacket;

AVPacket中的stream_index標記了該包是屬于音頻流還是視頻流,stream_index對應什么值的時候是屬于音頻流/視頻流呢?那就需要解析flv/mp4文件,我們可以通過以下方式獲得視頻流的相關信息:

	char* in_filename = "/home/yx/media_file/believe.flv";	// 定義媒體流路徑AVFormatContext *in_file_ctx = NULL;    // 媒體流上下文int videoindex = -1;                    // 視頻索引int audioindex = -1;                    // 音頻索引int result = avformat_open_input(&in_file_ctx,in_filename,NULL,NULL);   // 打開媒體流(將輸入文件與媒體流相關)result = avformat_find_stream_info(in_file_ctx,NULL);                   // 查找媒體流信息printf("stream number:%d\n",in_file_ctx->nb_streams);                   // 打印媒體流中流種類個數,一般只有兩個:音頻/視頻for(uint32_t i = 0;i < in_file_ctx->nb_streams; i++)                    // 遍歷兩個流{AVStream* in_stream = in_file_ctx->streams[i];                      // 指定視頻流文件中第i個流if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){printf("**********音頻流**********\n");printf("samplerate:%dHz\n",in_stream->codecpar->sample_rate);   // 采樣率printf("index:%d\n",in_stream->index);                          // 媒體流標簽printf("channel number:%d\n",in_stream->codecpar->channels);    // 聲道數if(in_stream->codecpar->format == AV_SAMPLE_FMT_FLTP)           // 采樣格式printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");else if(in_stream->codecpar->format == AV_SAMPLE_FMT_S16P)printf("sampleformat:AV_SAMPLE_FMT_S16P\n");if(in_stream->codecpar->codec_id == AV_CODEC_ID_AAC)            // 打印音頻流編碼格式printf("audio codec:AV_CODEC_ID_AAC\n");else if(in_stream->codecpar->codec_id == AV_CODEC_ID_MP3)printf("audio codec:AV_CODEC_ID_MP3\n");elseprintf("audio codec:%d\n",in_stream->codecpar->codec_id);if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration)*av_q2d(in_stream->time_base);printf("audio duration: %02d:%02d:%02d\n",duration_audio/3600,(duration_audio % 3600)/60,(duration_audio % 60));}elseprintf("audio duration unknown\n");audioindex = i;                                                 // 獲得音頻標簽													}else if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){printf("**********視頻流**********\n");printf("fps:%lffps\n",av_q2d(in_stream->avg_frame_rate));       // 幀率printf("index:%d\n",in_stream->index);                          // 媒體流標簽printf("width:%d,height:%d\n",in_stream->codecpar->width,in_stream->codecpar->height);    // 聲道數if(in_stream->codecpar->codec_id = AV_CODEC_ID_MPEG4)printf("video codec:MPEG4\n");else if(in_stream->codecpar->codec_id = AV_CODEC_ID_H264)printf("video codec:H264\n");elseprintf("video codec:%d\n",in_stream->codecpar->codec_id);if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration)*av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_audio/3600,(duration_audio % 3600)/60,(duration_audio % 60));}elseprintf("video duration unknown\n");videoindex = i;                                                 // 獲得視頻標簽}}

此時我們就獲得了解復用最關鍵的信息:視頻流標簽和音頻流標簽,接下來只需要依次讀取視頻流中的packet,依次判斷AVPacket中的stream_index來區分音頻或者視頻,這里先讀取20個packet進行分析:

AVPacket* pkt = av_packet_alloc();int pkt_count = 0;                                  // 當前是第0個包int print_count = 20;                               // 最大打印十個包的信息while(pkt_count<=20)                                // 只解析20個包{result = av_read_frame(in_file_ctx,pkt);        // 依次從輸入視頻來讀取包if(result < 0){printf("av_read_frame fail\n");break;}if(pkt_count++ < print_count){if(pkt->stream_index == audioindex){printf("audioindex:%d\n",audioindex);printf("audio pts: %lld\n", pkt->pts);printf("audio dts: %lld\n", pkt->dts);printf("audio size: %d\n", pkt->size);printf("audio pos: %lld\n", pkt->pos);printf("audio duration: %lf\n\n",pkt->duration * av_q2d(in_file_ctx->streams[audioindex]->time_base));}else if(pkt->stream_index == videoindex){printf("videoindex:%d\n",videoindex);printf("video pts: %lld\n", pkt->pts);printf("video dts: %lld\n", pkt->dts);printf("video size: %d\n", pkt->size);printf("video pos: %lld\n", pkt->pos);printf("video duration: %lf\n\n",pkt->duration * av_q2d(in_file_ctx->streams[videoindex]->time_base));}}av_packet_unref(pkt);                           // 解析完引用計數-1,自動釋放}

這里我們讀取到視頻包或者音頻包后,打印包的詳細信息:
pts:編碼時間戳,dts:解碼時間戳,size:包的大小,pos:包當前的位置。
每一個包的相關信息讀取之后,調用 av_packet_unref(pkt)使引用計數–,當計數減為0,系統會自動釋放該部分空間。
在這里插入圖片描述

完整代碼如下:

#include <stdio.h>
#include "libavformat/avformat.h"
void demux_flv()
{char* in_filename = "/home/yx/media_file/believe.flv";printf("輸入文件路徑%s\n",in_filename);AVFormatContext *in_file_ctx = NULL;    // 媒體流上下文int videoindex = -1;                    // 視頻索引int audioindex = -1;                    // 音頻索引int result = avformat_open_input(&in_file_ctx,in_filename,NULL,NULL);   // 打開媒體流(將輸入文件與媒體流相關)if(result < 0)printf("open file fail\n");result = avformat_find_stream_info(in_file_ctx,NULL);                   // 查找媒體流信息if(result < 0)printf("find stream info fail\n");av_dump_format(in_file_ctx,0,in_filename,0);                            // 打印輸出媒體流的信息,第1個0表示輸出所有流printf("media name:%s\n",in_file_ctx->url);printf("stream number:%d\n",in_file_ctx->nb_streams);                   // 只有兩個流:視頻流或者音頻流printf("media average radio:%lldkps\n",(int64_t)(in_file_ctx->bit_rate/1024));int total_seconds,hour,minute,second;total_seconds = (in_file_ctx->duration)/AV_TIME_BASE;hour = total_seconds/3600;minute = (total_seconds % 3600)/60;second = (total_seconds % 60);printf("total duration: %02d:%02d:%02d\n",hour,minute,second);for(uint32_t i = 0;i < in_file_ctx->nb_streams; i++)                    // 遍歷兩個流{AVStream* in_stream = in_file_ctx->streams[i];                      // 指定視頻流文件中第i個流if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){printf("**********音頻流**********\n");printf("samplerate:%dHz\n",in_stream->codecpar->sample_rate);   // 采樣率printf("index:%d\n",in_stream->index);                          // 媒體流標簽printf("channel number:%d\n",in_stream->codecpar->channels);    // 聲道數if(in_stream->codecpar->format == AV_SAMPLE_FMT_FLTP)           // 采樣格式printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");else if(in_stream->codecpar->format == AV_SAMPLE_FMT_S16P)printf("sampleformat:AV_SAMPLE_FMT_S16P\n");if(in_stream->codecpar->codec_id == AV_CODEC_ID_AAC)            // 打印音頻流編碼格式printf("audio codec:AV_CODEC_ID_AAC\n");else if(in_stream->codecpar->codec_id == AV_CODEC_ID_MP3)printf("audio codec:AV_CODEC_ID_MP3\n");elseprintf("audio codec:%d\n",in_stream->codecpar->codec_id);if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration)*av_q2d(in_stream->time_base);printf("audio duration: %02d:%02d:%02d\n",duration_audio/3600,(duration_audio % 3600)/60,(duration_audio % 60));}elseprintf("audio duration unknown\n");audioindex = i;                                                 // 獲得音頻標簽}else if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){printf("**********視頻流**********\n");printf("fps:%lffps\n",av_q2d(in_stream->avg_frame_rate));       // 幀率printf("index:%d\n",in_stream->index);                          // 媒體流標簽printf("width:%d,height:%d\n",in_stream->codecpar->width,in_stream->codecpar->height);    // 聲道數if(in_stream->codecpar->codec_id = AV_CODEC_ID_MPEG4)printf("video codec:MPEG4\n");else if(in_stream->codecpar->codec_id = AV_CODEC_ID_H264)printf("video codec:H264\n");elseprintf("video codec:%d\n",in_stream->codecpar->codec_id);if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration)*av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_audio/3600,(duration_audio % 3600)/60,(duration_audio % 60));}elseprintf("video duration unknown\n");videoindex = i;                                                 // 獲得視頻標簽}}printf("====================================\n");AVPacket* pkt = av_packet_alloc();int pkt_count = 0;                                  // 當前是第0個包int print_count = 20;                               // 最大打印十個包的信息while(pkt_count<=20)                                // 只解析20個包{result = av_read_frame(in_file_ctx,pkt);        // 依次從輸入視頻來讀取包if(result < 0){printf("av_read_frame fail\n");break;}if(pkt_count++ < print_count){if(pkt->stream_index == audioindex){printf("audioindex:%d\n",audioindex);printf("audio pts: %lld\n", pkt->pts);printf("audio dts: %lld\n", pkt->dts);printf("audio size: %d\n", pkt->size);printf("audio pos: %lld\n", pkt->pos);printf("audio duration: %lf\n\n",pkt->duration * av_q2d(in_file_ctx->streams[audioindex]->time_base));}else if(pkt->stream_index == videoindex){printf("videoindex:%d\n",videoindex);printf("video pts: %lld\n", pkt->pts);printf("video dts: %lld\n", pkt->dts);printf("video size: %d\n", pkt->size);printf("video pos: %lld\n", pkt->pos);printf("video duration: %lf\n\n",pkt->duration * av_q2d(in_file_ctx->streams[videoindex]->time_base));}}av_packet_unref(pkt);                           // 解析完引用計數-1,自動釋放}
}int main()
{demux_flv();printf("Hello World!\n");return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38424.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38424.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38424.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

指針賦值與引用傳遞:C語言的基礎知識與實踐技巧

指針賦值與引用傳遞&#xff1a;C語言的基礎知識與實踐技巧 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; **1. **引言 在C語言中&#xff0c;指針是一種強…

Vue CLI VS Vite

Vue CLI與Vite區別&#xff1a; Vue CLI與Vite之間存在明顯的區別&#xff0c;這些區別主要體現在實現原理、優化策略、開發環境速度、構建速度、依賴關系分析和插件系統等方面。以下是關于Vue CLI和Vite區別的詳細分析&#xff1a; 實現原理&#xff1a; Vue CLI&#xff1a…

【Spring Boot】Spring Boot簡介

1、概述 Spring Boot是一個用于創建獨立、生產級別的基于Spring的應用程序的開發框架。旨在簡化Spring應用的初始搭建和開發過程。它通過自動配置和大量默認配置&#xff0c;使得開發者能夠快速搭建一個獨立的Spring應用&#xff0c;無需進行大量的手動配置。 2、主要特點 快…

【一篇搞懂】操作系統期末大題:進程同步與互斥 PV操作

文章目錄 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;??????題型一&#xff1a;利用信號量實現前驅關系題型二&#xff1a;利用信號量實現資源同步與互斥 一、前言&#x1f680;&#x1f680;&#x1f680; 本文簡介&#xff1a;這是一篇基于b…

無人機遠程控制:北斗短報文技術詳解

無人機&#xff08;UAV&#xff09;技術的快速發展和應用&#xff0c;使得遠程控制成為了一項關鍵技術。無人機遠程控制涉及無線通信、數據處理等多個方面&#xff0c;其中北斗短報文技術以其獨特的優勢&#xff0c;在無人機遠程控制領域發揮著重要作用。本文將詳細解析無人機遠…

2024-06-26 base SAS programming 學習筆記6(proc report)

proc report可以生成報表&#xff0c;基本格式&#xff1a; proc report data options; (options 可以是windows/WD表示將結果輸出至單獨的報表窗口&#xff0c;或者nowindows/nowd將結果輸出至HTML結果窗口) column variables ;(篩選待輸出的變量&#xff0c;變量名與變量名之…

09_計算機網絡模型

目錄 OSI/RM七層模型 OSI/RM七層模型 各層介紹及硬件設備 傳輸介質 TCP/IP協議簇 網絡層協議 傳輸層協議 應用層協議 完整URL的組成 IP地址表示與計算 分類地址格式 子網劃分和超網聚合 無分類編址 特殊含義的IP地址 IPv6協議 過渡技術 OSI/RM七層模型 OSI/RM七…

區間動態規劃——最長回文子序列長度(C++)

把夜熬成粥&#xff0c;然后喝了它。 ——2024年7月1日 書接上回&#xff1a;區間動態規劃——最長回文子串&#xff08;C&#xff09;-CSDN博客&#xff0c;大家有想到解決辦法嗎&#xff1f; 題目描述 給定一個字符串s&#xff08;s僅由數字和英文大小寫字母組成&#xff0…

微積分-導數3(微分法則)

常見函數的導數 常量函數的導數 d d x ( c ) 0 \frac{d}{dx}(c) 0 dxd?(c)0 常量函數的圖像是一條水平線 y c y c yc&#xff0c;它的斜率為0&#xff0c;所以我們必須有 f ′ ( x ) 0 f(x) 0 f′(x)0。從導數的定義來看&#xff0c;證明也很簡單&#xff1a; f ′ …

在node.js環境中使用web服務器http-server運行html靜態文件

http-server http-server是一個超輕量級web服務器&#xff0c;它可以將任何一個文件夾當作服務器的目錄供自己使用。 當我們想要在服務器運行一些代碼&#xff0c;但是又不會配置服務器的時候&#xff0c;就可以使用http-server就可以搞定了。 使用方法 因為http-server需要…

Linux Vim 進階教程

Linux Vim 進階教程 1. 簡介 Vim&#xff08;Vi IMproved&#xff09;是一款功能強大的文本編輯器&#xff0c;廣泛應用于Linux和Unix系統中。本教程將深入探討Vim的高級功能和技巧&#xff0c;幫助您提升編輯效率和使用體驗。 2. Vim 配置和插件管理 2.1 配置文件 .vimrc …

QT拖放事件之三:自定義拖放操作-利用QDrag來拖動完成數據的傳輸

1、運行效果 1)Qt::MoveAction 2)Qt::CopyAction 2、源碼 #include "Widget.h" #include "ui_Widget.h" #include "common.h"

二級建造師(建筑工程專業)考試題庫,高效備考!!!

16.在施工合同履行期間發生的變更事項中&#xff0c;屬于工程變更的是&#xff08;&#xff09;。 A.質量要求變更 B.分包單位變更 C.合同價款變更 D.相關法規變更 答案&#xff1a;A 解析&#xff1a;工程變更一般是指在工程施工過程中&#xff0c;根據合同約定對施工的…

練習 String翻轉 注冊處理 字符串統計

p493 將字符串中指定部分進行翻轉 package chapter;public class reverse {public static void main(String[] args) {String str "abcdef";str reverseMethod(str,0,3);System.out.println(str);}public static String reverseMethod(String str, int start, in…

恭賀甘露海首屆道教南宗養生論壇暨天臺山第十屆道醫大會圓滿成功

6月13日&#xff0c;首屆中國道教南宗養生論壇暨天臺山第十屆道醫學術交流大會在浙江新昌重陽宮千人會場隆重開幕。 本次大會主辦單位&#xff1a;天臺山桐柏宮 中國民間中醫醫藥研究開發協會道醫學分會&#xff0c; 承辦單位&#xff1a;新昌縣重陽宮 &#xff0c;協辦單位&…

網絡基礎:靜態路由

靜態路由是一種由網絡管理員手動配置的路由方式&#xff0c;用于在網絡設備&#xff08;如路由器或交換機&#xff09;之間傳遞數據包。與動態路由不同&#xff0c;靜態路由不會根據網絡狀態的變化自動調整。 不同廠商的網絡設備在靜態路由的配置上有些許差異&#xff1b;下面…

什么是以太坊合約ABI(Application Binary Interface)

文章目錄 什么是以太坊合約ABI一、背景二、ABI&#xff08;Application Binary Interface&#xff09;三、怎么生成ABIsolc命令 四、abi內容FunctionEvent函數選擇器 五、參考 什么是以太坊合約ABI 一、背景 以太坊的智能合約程序&#xff0c;是在以太坊虛擬機&#xff08;Et…

網絡構建關鍵技術_2.IPv4與IPv6融合組網技術

互聯網數字分配機構&#xff08;IANA&#xff09;在2016年已向國際互聯網工程任務組&#xff08;IETF&#xff09;提出建議&#xff0c;要求新制定的國際互聯網標準只支持IPv6&#xff0c;不再兼容IPv4。目前&#xff0c;IPv6已經成為唯一公認的下一代互聯網商用解決方案&#…

安卓開發app-基礎的java項目構建補充知識

安卓開發app-基礎的java項目構建補充知識&#xff01;上一次分享了基礎的項目構建&#xff0c;但是還遺漏了一些基礎的內容。今天補充完整。 首先&#xff0c;是關于項目的一些配置文件的信息。 第一個配置文件&#xff1a;{setting.gradle} 國內阿里云倉庫地址信息&#xff1…

定制型汽車傳感器在汽車中的應用

定制型汽車霍爾傳感器在汽車中的應用及功能 曲軸和凸輪軸位置傳感器&#xff1a; 這些傳感器用于監測發動機的曲軸和凸輪軸的位置&#xff0c;幫助發動機管理系統精確控制點火時機和燃油噴射&#xff0c;提高發動機效率。 變速器控制系統&#xff1a; 在自動變速器中&#xf…