FFPLAY的原理(一)

概要

電影文件有很多基本的組成部分。首先,文件本身被稱為容器Container,容器的類型決定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接著,你有一組流,例如,你經常有的是一個音頻流和一個視頻流。(一個流只是一種想像出來的詞語,用來表示一連串的通過時間來串連的數據元素)。在流中的數據元素被稱為幀Frame。每個流是由不同的編碼 器來編碼生成的。編解碼器 描述了實際的數據是如何被編碼Coded和解碼DECoded的,因此它的名字叫做CODEC。Divx和 MP3就是編解碼器的例子。接著從流中被讀出來的叫做包Packets。包是一段數據,它包含了一段可以被解碼成方便我們最后在應用程序中操作的原始幀的數據。根據我們的目的,每個包包含了完整的幀或者對于音頻來說是許多格式的完整幀。

基本上來說,處理視頻和音頻流是很容易的:

10 從video.avi文件中打開視頻流video_stream

20 從視頻流中讀取包到幀中

30 如果這個幀還不完整,跳到20

40 對這個幀進行一些操作

50 跳回到20

在這個程序中使用ffmpeg來處理多種媒體是相當容易的,雖然很多程序可能在對幀進行操作的時候非常的復雜。因此在這篇指導中,我們將打開一個文件,讀取里面的視頻流,而且我們對幀的操作將是把這個幀寫到一個PPM文件中。

打開文件

首先,來看一下我們如何打開一個文件。通過ffmpeg,你必需先初始化這個庫。(注意在某些系統中必需用<ffmpeg/avcodec.h>和<ffmpeg/avformat.h>來替換)

#include <avcodec.h>

#include <avformat.h>

...

int main(int argc, charg *argv[]) {

av_register_all();

這里注冊了所有的文件格式和編解碼器的庫,所以它們將被自動的使用在被打開的合適格式的文件上。注意你只需要調用 av_register_all()一次,因此我們在主函數main()中來調用它。如果你喜歡,也可以只注冊特定的格式和編解碼器,但是通常你沒有必要這樣做。

現在我們可以真正的打開文件:

AVFormatContext *pFormatCtx;

// Open video file

if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)

return -1; // Couldn't open file

我們通過第一個參數來獲得文件名。這個函數讀取文件的頭部并且把信息保存到我們給的AVFormatContext結構體中。最后三個參數用來指定特殊的文件格式,緩沖大小和格式參數,但如果把它們設置為空NULL或者0,libavformat將自動檢測這些參數。

這個函數只是檢測了文件的頭部,所以接著我們需要檢查在文件中的流的信息:

// Retrieve stream information

if(av_find_stream_info(pFormatCtx)<0)

return -1; // Couldn't find stream information

這個函數為pFormatCtx->streams填充上正確的信息。我們引進一個手工調試的函數來看一下里面有什么:

// Dump information about file onto standard error

dump_format(pFormatCtx, 0, argv[1], 0);

現在pFormatCtx->streams僅僅是一組大小為pFormatCtx->nb_streams的指針,所以讓我們先跳過它直到我們找到一個視頻流。

int i;

AVCodecContext *pCodecCtx;

// Find the first video stream

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)

if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {

videoStream=i;

break;

}

if(videoStream==-1)

return -1; // Didn't find a video stream

// Get a pointer to the codec context for the video stream

pCodecCtx=pFormatCtx->streams[videoStream]->codec;

流中關于編解碼器的信息就是被我們叫做"codec context"(編解碼器上下文)的東西。這里面包含了流中所使用的關于編解碼器的所有信息,現在我們有了一個指向他的指針。但是我們必需要找到真正的編解碼器并且打開它:

AVCodec *pCodec;

// Find the decoder for the video stream

pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

if(pCodec==NULL) {

fprintf(stderr, "Unsupported codec!/n");

return -1; // Codec not found

}

// Open codec

if(avcodec_open(pCodecCtx, pCodec)<0)

return -1; // Could not open codec

有些人可能會從舊的指導中記得有兩個關于這些代碼其它部分:添加CODEC_FLAG_TRUNCATED到 pCodecCtx->flags和添加一個hack來粗糙的修正幀率。這兩個修正已經不在存在于ffplay.c中。因此,我必需假設它們不再必要。我們移除了那些代碼后還有一個需要指出的不同點:pCodecCtx->time_base現在已經保存了幀率的信息。time_base是一個結構體,它里面有一個分子和分母 (AVRational)。我們使用分數的方式來表示幀率是因為很多編解碼器使用非整數的幀率(例如NTSC使用29.97fps)。

保存數據

現在我們需要找到一個地方來保存幀:

AVFrame *pFrame;

// Allocate video frame

pFrame=avcodec_alloc_frame();

因為我們準備輸出保存24位RGB色的PPM文件,我們必需把幀的格式從原來的轉換為RGB。FFMPEG將為我們做這些轉換。在大多數項目中(包括我們的這個)我們都想把原始的幀轉換成一個特定的格式。讓我們先為轉換來申請一幀的內存。

// Allocate an AVFrame structure

pFrameRGB=avcodec_alloc_frame();

if(pFrameRGB==NULL)

return -1;

即使我們申請了一幀的內存,當轉換的時候,我們仍然需要一個地方來放置原始的數據。我們使用avpicture_get_size來獲得我們需要的大小,然后手工申請內存空間:

uint8_t *buffer;

int numBytes;

// Determine required buffer size and allocate buffer

numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,

pCodecCtx->height);

buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

av_malloc是ffmpeg的malloc,用來實現一個簡單的malloc的包裝,這樣來保證內存地址是對齊的(4字節對齊或者2字節對齊)。它并不能保護你不被內存泄漏,重復釋放或者其它malloc的問題所困擾。

現在我們使用avpicture_fill來把幀和我們新申請的內存來結合。關于AVPicture的結成:AVPicture結構體是AVFrame結構體的子集――AVFrame結構體的開始部分與AVPicture結構體是一樣的。

// Assign appropriate parts of buffer to image planes in pFrameRGB

// Note that pFrameRGB is an AVFrame, but AVFrame is a superset

// of AVPicture

avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,

pCodecCtx->width, pCodecCtx->height);

最后,我們已經準備好來從流中讀取數據了。

讀取數據

我們將要做的是通過讀取包來讀取整個視頻流,然后把它解碼成幀,最好后轉換格式并且保存。

int frameFinished;

AVPacket packet;

i=0;

while(av_read_frame(pFormatCtx, &packet)>=0) {

// Is this a packet from the video stream?

if(packet.stream_index==videoStream) {

// Decode video frame

avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,

packet.data, packet.size);

// Did we get a video frame?

if(frameFinished) {

// Convert the image from its native format to RGB

img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,

(AVPicture*)pFrame, pCodecCtx->pix_fmt,

pCodecCtx->width, pCodecCtx->height);

// Save the frame to disk

if(++i<=5)

SaveFrame(pFrameRGB, pCodecCtx->width,

pCodecCtx->height, i);

}

}

// Free the packet that was allocated by av_read_frame

av_free_packet(&packet);

}

這個循環過程是比較簡單的:av_read_frame()讀取一個包并且把它保存到AVPacket結構體中。注意我們僅僅申請了一個包的結構體 ――ffmpeg為我們申請了內部的數據的內存并通過packet.data指針來指向它。這些數據可以在后面通過av_free_packet()來釋放。函數avcodec_decode_video()把包轉換為幀。然而當解碼一個包的時候,我們可能沒有得到我們需要的關于幀的信息。因此,當我們得到下一幀的時候,avcodec_decode_video()為我們設置了幀結束標志frameFinished。最后,我們使用 img_convert()函數來把幀從原始格式(pCodecCtx->pix_fmt)轉換成為RGB格式。要記住,你可以把一個 AVFrame結構體的指針轉換為AVPicture結構體的指針。最后,我們把幀和高度寬度信息傳遞給我們的SaveFrame函數。

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

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

相關文章

安卓開發 新浪微博share接口實現發帶本地圖片的微博

1.微博share接口 在開始之前&#xff0c;我們先看一下要用到的這個接口&#xff1a; 我們這次是要上傳本地圖片&#xff0c;可以很明確的知道&#xff0c;除了要用POST方式提交請求&#xff0c;還要采用multipart/form-data編碼方式。 那么這個multipart/form-data編碼方式是什…

python編寫裝飾器_我也來寫一下python裝飾器

有借用&#xff0c;但原文出處已經找不到了&#xff0c;根據筆記分享一下解釋器的基礎。下面的代碼表示&#xff0c;等待兩秒鐘&#xff0c;輸出‘test is running。現在要求增加統計程序運行時間的功能。等待兩秒鐘&#xff0c;輸出‘test is running,現要求增加統計程序運行時…

VirtualBox安裝Centos6.8出現——E_INVALIDARG (0x80070057)

VirtualBox使用已有的虛擬硬盤出錯&#xff1a; 問題描述&#xff1a;UUID已經存在 Cannot register the hard disk E:\system_iso\centos6.8.vdi {05f096aa-67fc-4191-983d-1ed00fc6cce9} because a hard disk E:\system_iso\centos68_02\centos6.8.vdi with UUID {05f096aa-6…

DFT 與 ATPG綜 述

DFT 可測試性設計 工程會接觸 DFT。需要了解 DFT 知識&#xff0c;但不需要深入。 三種基本的測試&#xff08;概念來自參考文檔&#xff09;&#xff1a; 邊界掃描測試&#xff1a;Boundary Scan Test: 測試目標是 IO-PAD&#xff0c;利用 JTAG 接口互連以方便 測試。&#x…

非線性動力學_非線性動力學特輯 低維到高維的聯通者

序言&#xff1a; 本文將以維度為主線&#xff0c; 帶量大家進入非線性動力學的世界。 文章數學部分不需要全部理解&#xff0c; 理解思維方法為主非線性動力學&#xff0c;是物理學的思維進入傳統方法所不能解決的問題的一座豐碑。它可以幫助我們理解不同復雜度和時間空間尺度…

Go語言channel與select原理

本文會嘗試解釋 go runtime 中 channel 和 select 的具體實現&#xff0c;部分內容來自 gophercon2017。Go版本為1.8.3channel 第一部分講述一下 channel 的用法。channel 可以看做一個隊列&#xff0c;用于多個goroutine之間的通信&#xff0c;例如下面的例子&#xff0c;一個…

Xadmin添加用戶小組件出錯

環境&#xff1a; Python 3.5.6 Django 2.1 Xadmin 原因&#xff1a; render函數在django2.1上有變化 解決方案&#xff1a; 1.在Python終端輸入命令help(xadmin) 查看xadmin安裝位置 得到如下輸出 FILE/root/anaconda3/envs/learndjango/lib/python3.5/site-packages/xad…

成本預算的四個步驟_全網推廣步驟有哪些?

全網推廣的步驟是什么&#xff1f;一般來說&#xff0c;搜索引擎優化是大多數中小企業常用的推廣方法。主要是通過對一些搜索引擎的排名來提高網站的曝光率&#xff0c;從而更好的提高自己網站的流量&#xff0c;從而更好的實現互聯網層面的銷售。接下來&#xff0c;讓我們學習…

undefined reference to `std::cout'等錯誤

&#xff08;1&#xff09;gcc和g都是GNU(組織)的一個編譯器。 &#xff08;2&#xff09;后綴名為.c的程序和.cpp的程序g都會當成是c的源程序來處理。而gcc不然&#xff0c;gcc會把.c的程序處理成c程序。 &#xff08;3&#xff09;對于.cpp的程序&#xff0c;編譯可以用gcc/g…

FFPLAY的原理(二)

關于包Packets的注釋從技術上講一個包可以包含部分或者其它的數據&#xff0c;但是ffmpeg的解釋器保證了我們得到的包Packets包含的要么是完整的要么是多種完整的幀。現在我們需要做的是讓SaveFrame函數能把RGB信息定稿到一個PPM格式的文件中。我們將生成一個簡單的PPM格式文件…

python生成requirements.txt的兩種方法

python項目如何在另一個環境上重新構建項目所需要的運行環境依賴包&#xff1f; 使用的時候邊記載是個很麻煩的事情&#xff0c;總會出現遺漏的包的問題&#xff0c;這個時候手動安裝也很麻煩&#xff0c;不能確定代碼報錯的需要安裝的包是什么版本。這些問題&#xff0c;requi…

node.js 安裝使用http-server

node.js npm全局安裝了http-server后我該怎么使用它&#xff1f;我在它的安裝目錄下創建了inde.html&#xff0c;瀏覽器localhost:8080可以訪問&#xff0c;那我的項目需要放在它的安裝目錄下&#xff1f;還是需要在我的項目下配置什么或者使用什么指令啟動它&#xff1f;我在我…

D - 卿學姐與魔法

卿學姐與魔法 Time Limit: 1200/800MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit Status“你的膜法也救不了你 在去拯救公主的道路上&#xff0c;卿學姐披荊斬棘&#xff0c;刀刃早已銹跡斑斑。 一日卿學姐正在為武器的問題發愁&#xff0c;碰到了正…

python對excel表統計視頻教程_Python實現對excel文件列表值進行統計的方法

本文實例講述了Python實現對excel文件列表值進行統計的方法。分享給大家供大家參考。具體如下&#xff1a;#!/usr/bin/env python#codinggbk#此PY用來統計一個execl文件中的特定一列的值的分類import win32com.clientfilenameraw_input("請輸入要統計文件的詳細地址&#…

mooc后臺管理系統設計

摘 要 本設計采用Python中的Django框架實現Mooc后臺管理界面設計,django是一個完整的開源web開源框架,使用起來能夠快速的搭建你想要的網站,由于django自帶后臺管理系統,本設計中后臺管理模板采用功能更加強大的Xadmin實現。數據庫部分采用mysql5.7,由于django中有自帶封裝的數…

DirectShow系統初級指南

流媒體的處理&#xff0c;以其復雜性和技術性&#xff0c;一向廣受工業界的關注。特別伴隨著因特網的普及&#xff0c;流媒體在網絡上的廣泛應用&#xff0c;怎樣使流媒體的處理變得簡單而富有成效逐漸成為了焦點問題。選擇一種合適的應用方案&#xff0c;事半功倍。此時&#…

正則正整數含0

^0?$|^([1-9][0-9]*)?$

MySQL 數據庫導出導入操作

有時需要將 MySQL 數據庫中的數據導入到其它的數據庫中&#xff0c;這里以從 Ubuntu 系統的 MySQL 數據庫導出 zabbix 這個數據庫到 Windows 系統中的MySQL 為例。 導出數據庫 導出數據其實非常方便&#xff0c;比如將 MySQL 中的 zabbix 這個數據庫導出到當前文件夾&#xff…

您的apple id 暫時不符合使用此應用程序_Mac相機不工作時該怎么辦

蘋果公司的許多臺式機和筆記本電腦都包含一個內置網絡攝像頭&#xff0c;該公司愉快地將其稱為FaceTime相機。但是&#xff0c;如果您的Mac網絡攝像頭無法正常工作&#xff0c;并且在嘗試訪問它時顯示為斷開連接或不可用&#xff0c;則您可能不會感到高興。您可以嘗試以下操作來…

基于DirectShow的流媒體解碼和回放

一、 前言  流媒體的定義很廣泛&#xff0c;大多數時候指的是把連續的影像和聲音信息經過壓縮處理后放上網站服務器&#xff0c;讓用戶一邊下載一邊觀看、收聽&#xff0c;而不需要等整個壓縮文件下載到自己機器就可以觀看的視頻/音頻傳輸、壓縮技術。流媒體也指代由這種技術…