ffplay for mfc 代碼備忘錄

在上傳一個開源播放器項目ffplay for mfc。它會ffmpeg工程ffplay媒體播放器(ffplay.c)移植到VC環境,而使用MFC作一套接口。它能夠完成一個播放器播放的基本流程的視頻:解決方案協議。解包,視頻/音頻解碼,AV同步。視頻和音頻輸出。網絡

此外還包含一些控制功能:播放,暫停/繼續,前進,後退,中止,逐幀播放,全屏等;以及一些碼流分析功能:視頻解碼分析和音頻解碼分析。數據結構

具體的軟件使用就不細緻介紹了,本文簡介當中比較重要的模塊的流程。以防長時間不看的話忘了~ide

軟件信息:函數

ffplay播放器移植VC的工程:ffplay for MFCoop

SourceForge項目主頁:
https://sourceforge.net/projects/ffplayformfc/源碼分析

1. 軟件結構

軟件結構如圖1所看到的。包含例如如下模塊:控制,視頻播放。參數提取。碼流分析。當中,視頻播放模塊用於視頻的解碼和播放;控制模塊用於控制視頻的播放。參數提取模塊用於提取顯示視頻的各類參數;碼流分析模塊伴隨着視頻的播放分析視音頻流中的參數。學習

 

圖1.軟件結構ui

2.模塊說明

2.1.  視頻播放模塊

視頻播放模塊的做用就是將網絡上(或者是本地)的視音頻數據接收下來通過一系列處理後終於輸出到視音頻設備上。依據處理的順序不一樣,它可以分爲下面幾個子模塊:編碼

1)      解協議模塊.net

2)      解封裝模塊

3)      視頻解碼模塊

4)      音頻解碼模塊

5)      視音頻同步模塊

視頻播放模塊的流程圖如圖2所看到的。依照處理的順序分爲解協議,解封裝,視頻解碼,音頻解碼,視音頻同步。

具體的原理在文章 [總結]視音頻編解碼技術零基礎學習方法 中有具體的說明,在這裏再也不反覆,示意圖例如如下所看到的。


 

圖2.視頻播放模塊流程

這一模塊主要是經過對ffplay.c改寫而獲得的。

改寫完畢後爲ffplaycore.cpp。

簡單的代碼方面的流程可以參考:

100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器

比較完整的代碼方面的流程可以參考:

FFplay源碼分析:整體流程圖

2.2.  控制模塊

控制模塊的做用就是控制視頻的播放。包含下面幾種功能:

1)      開始

2)      暫停/繼續

3)      快進/快退

4)      逐幀播放

5)      調整窗體大小

6)      全屏

7)      調整播放進度

軟件開始解碼視頻數據以後,會進入一個函數event_loop()。該函數內部不停地循環,使用SDL_WaitEvent()等待着響應系統的消息。接收到消息以後,依據消息類型的不一樣,作出不一樣的響應。如圖3所看到的例舉了6種不一樣的消息相應的不一樣的響應:

1)      SDLK_ESCAPE。相應鍵盤上「Esc」鍵的響應。功能是退出程序。

2)      SDLK_SPACE。相應鍵盤上「空格」鍵的響應。功能是暫停播放。

3)      SDL_MOUSEBUTTONDOWN。相應鼠標單擊的響應。功能是調整視頻播放進度。

4)      SDL_VIDEORESIZE。

相應「VideoResize」消息的響應。功能是調整播放窗體的大小。

5)      FF_REFRESH_EVENT。

相應本身定義消息「FF_REFRESH_EVENT」的響應。

功能是刷新視頻畫面。

6)      FFMFC_SEEK_BAR_EVENT。相應本身定義消息「FFMFC_SEEK_BAR_EVENT」的響應。功能是調整視頻播放進度條。

 

圖3.控制模塊流程(消息循環)

event_loop()函數代碼例如如下:

/* handle an event sent by the GUI */
//處理各類鼠標鍵盤命令,包含各類事件
static void event_loop(VideoState *cur_stream)
{
	SDL_Event event;
	double incr, pos, frac;

	for (;;) {

		double x;
		//推斷退出-------
		if(exit_remark==1)
			break;
		//---------------
		if (cur_stream->abort_request)
			break;

		SDL_WaitEvent(&event);
		switch (event.type) {
		case SDL_KEYDOWN:
			if (exit_on_keydown) 
			{
				do_exit(cur_stream);
				break;
			}
			switch (event.key.keysym.sym) {
			case SDLK_ESCAPE:
			case SDLK_q:
				do_exit(cur_stream);
				break;
			case SDLK_f:
				//全屏
				toggle_full_screen(cur_stream);
				cur_stream->force_refresh = 1;
				break;
			case SDLK_p:
				//暫停
			case SDLK_SPACE:
				toggle_pause(cur_stream);
				break;
			case SDLK_s: // S: Step to next frame
				step_to_next_frame(cur_stream);
				break;
			case SDLK_a:
				stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
				break;
			case SDLK_v:
				stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
				break;
			case SDLK_t:
				stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
				break;
				//改動了一下,三中顯示模式分紅了三個鍵
			case SDLK_w:
				toggle_audio_display(cur_stream,SHOW_MODE_VIDEO);
				cur_stream->force_refresh = 1;
				break;
			case SDLK_e:
				toggle_audio_display(cur_stream,SHOW_MODE_WAVES);
				cur_stream->force_refresh = 1;
				break;
			case SDLK_r:
				toggle_audio_display(cur_stream,SHOW_MODE_RDFT);
				cur_stream->force_refresh = 1;
				break;
			case SDLK_y:
				cur_stream->v_show_mode=SHOW_MODE_Y;
				break;
			case SDLK_PAGEUP:
				incr = 600.0;
				goto do_seek;
			case SDLK_PAGEDOWN:
				incr = -600.0;
				goto do_seek;
				//左方向鍵
			case SDLK_LEFT:
				incr = -10.0;
				goto do_seek;
			case SDLK_RIGHT:
				incr = 10.0;
				goto do_seek;
			case SDLK_UP:
				incr = 60.0;
				goto do_seek;
			case SDLK_DOWN:
				incr = -60.0;
do_seek:
				if (seek_by_bytes) {
					if (cur_stream->video_stream >= 0 && cur_stream->video_current_pos >= 0) {
						pos = cur_stream->video_current_pos;
					} else if (cur_stream->audio_stream >= 0 && cur_stream->audio_pkt.pos >= 0) {
						pos = cur_stream->audio_pkt.pos;
					} else
						pos = avio_tell(cur_stream->ic->pb);
					if (cur_stream->ic->bit_rate)
						incr *= cur_stream->ic->bit_rate / 8.0;
					else
						incr *= 180000.0;
					pos += incr;
					stream_seek(cur_stream, pos, incr, 1);
				} else {
					pos = get_master_clock(cur_stream);
					pos += incr;
					stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
				}
				break;
			default:
				break;
			}
			break;
		case SDL_VIDEOEXPOSE:
			cur_stream->force_refresh = 1;
			break;
			//鼠標單擊
		case SDL_MOUSEBUTTONDOWN:
			if (exit_on_mousedown) {
				do_exit(cur_stream);
				break;
			}
		case SDL_MOUSEMOTION:
			if (event.type == SDL_MOUSEBUTTONDOWN) {
				x = event.button.x;
			} else {
				if (event.motion.state != SDL_PRESSED)
					break;
				x = event.motion.x;
			}
			if (seek_by_bytes || cur_stream->ic->duration <= 0) {
				uint64_t size =  avio_size(cur_stream->ic->pb);
				stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
			} else {
				int64_t ts;
				int ns, hh, mm, ss;
				int tns, thh, tmm, tss;
				tns  = cur_stream->ic->duration / 1000000LL;
				thh  = tns / 3600;
				tmm  = (tns % 3600) / 60;
				tss  = (tns % 60);
				frac = x / cur_stream->width;
				ns   = frac * tns;
				hh   = ns / 3600;
				mm   = (ns % 3600) / 60;
				ss   = (ns % 60);
				fprintf(stderr, "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d)       \n", frac*100,
					hh, mm, ss, thh, tmm, tss);
				ts = frac * cur_stream->ic->duration;
				if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
					ts += cur_stream->ic->start_time;
				stream_seek(cur_stream, ts, 0, 0);
			}
			break;
		case SDL_VIDEORESIZE:
			screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0,
				SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL);
			screen_width  = cur_stream->width  = event.resize.w;
			screen_height = cur_stream->height = event.resize.h;
			cur_stream->force_refresh = 1;
			break;
		case SDL_QUIT:
		case FF_QUIT_EVENT:
			do_exit(cur_stream);
			break;
		case FF_ALLOC_EVENT:
			alloc_picture((VideoState *)(event.user.data1));
			break;
		case FF_REFRESH_EVENT:
			video_refresh(event.user.data1);
			cur_stream->refresh = 0;
			break;
		case FFMFC_SEEK_BAR_EVENT:{
			if (seek_by_bytes || cur_stream->ic->duration <= 0) {
				uint64_t size =  avio_size(cur_stream->ic->pb);
				stream_seek(cur_stream, size*seek_bar_pos/1000, 0, 1);
			} else {
				int64_t ts;
				frac=(double)seek_bar_pos/1000;
				ts = frac * cur_stream->ic->duration;
				if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
					ts += cur_stream->ic->start_time;
				stream_seek(cur_stream, ts, 0, 0);
			}
			break;
							   }

		default:
			break;
		}
	}
}

控制模塊的各個功能函數,僅僅需要設置必定內容的消息。再發送出去。就可以完畢相應的控制功能。

如圖4所看到的,分別例舉了3種控制功能的完畢方式。

1)      「暫停」功能,發送SDLK_SPACE消息。

2)      「調整窗體大小」功能。發送VIDEORESIZE消息,並附帶窗體的大小。

3)      「調整視頻播放進度條」功能。發送FFMFC_SEEK_BAR_EVENT消息。

 

圖4.控制模塊流程(發送消息)

各個功能函數的代碼例如如下:

//發送「全屏」命令
//Send Command "FullScreen"
void ffmfc_play_fullcreen(){
	SDL_Event event;
	event.type = SDL_KEYDOWN;
	event.key.keysym.sym=SDLK_f;
	SDL_PushEvent(&event);
}

//發送「暫停」命令
//Send Command "Pause"
void ffmfc_play_pause(){
	SDL_Event event;
	event.type = SDL_KEYDOWN;
	event.key.keysym.sym=SDLK_p;
	SDL_PushEvent(&event);
}

//發送「逐幀」命令
//Send Command "Step"
void ffmfc_seek_step(){
	SDL_Event event;
	event.type = SDL_KEYDOWN;
	event.key.keysym.sym=SDLK_s;
	SDL_PushEvent(&event);
}

//發送「寬高比」命令
//Send Command "AspectRatio"
void ffmfc_aspectratio(int num,int den){
	int w=g_is->width;
	int h=g_is->height;
	int w_re=h*num/den;
	SDL_Event event;
	event.type = SDL_VIDEORESIZE;
	event.resize.w=w_re;
	event.resize.h=h;
	SDL_PushEvent(&event);
}

//發送「大小」命令
//Send Command "WindowSize"
void ffmfc_size(int percentage){
	int w=g_is->ic->streams[g_is->video_stream]->codec->width;
	int h=g_is->ic->streams[g_is->video_stream]->codec->height;
	SDL_Event event;
	event.type = SDL_VIDEORESIZE;
	event.resize.w=w*percentage/100;
	event.resize.h=h*percentage/100;
	SDL_PushEvent(&event);
}

//發送「窗體畫面內容」命令
//Send Command "Audio Display Mode"
void ffmfc_audio_display(int mode){

	SDL_Event event;
	event.type = SDL_KEYDOWN;
	switch(mode){
	case 0:event.key.keysym.sym=SDLK_w;break;
	case 1:event.key.keysym.sym=SDLK_e;break;
	case 2:event.key.keysym.sym=SDLK_r;break;
	}
	SDL_PushEvent(&event);
}

//發送「前進/後退」命令
//Send Command "Seek"
void ffmfc_seek(int time){
	SDL_Event event;
	event.type = SDL_KEYDOWN;
	switch (time){
	case -10 :event.key.keysym.sym=SDLK_LEFT;break;
	case 10 :event.key.keysym.sym=SDLK_RIGHT;break;
	case -60 :event.key.keysym.sym=SDLK_DOWN;break;
	case 60 :event.key.keysym.sym=SDLK_UP;break;
	case -600 :event.key.keysym.sym=SDLK_PAGEDOWN;break;
	case 600 :event.key.keysym.sym=SDLK_PAGEUP;break;
	default :event.key.keysym.sym=SDLK_RIGHT;break;
	}
	SDL_PushEvent(&event);
}

//播放進度
//Seek Bar
void ffmfc_seek_bar(int pos){
	SDL_Event event;
	event.type = FFMFC_SEEK_BAR_EVENT;
	seek_bar_pos=pos;
	SDL_PushEvent(&event);
}

MFC中按鈕,進度條控件的消息響應函數僅僅要調用以上功能函數就可以實現相應的功能:

void CffplaymfcDlg::OnBnClickedSeekB()
{
	ffmfc_seek(-60);
}


void CffplaymfcDlg::OnBnClickedPause()
{
	ffmfc_play_pause();
}


void CffplaymfcDlg::OnBnClickedSeekF()
{
	ffmfc_seek(60);
}


void CffplaymfcDlg::OnBnClickedStop()
{
	ffmfc_quit();
	SystemClear();
	ResetBtn();
}


void CffplaymfcDlg::OnBnClickedSeekStep()
{
	ffmfc_seek_step();
}


void CffplaymfcDlg::OnBnClickedFullscreen()
{
	ffmfc_play_fullcreen();
}


2.3.參數提取模塊

參數提取模塊的做用就是提取視頻碼流中的一部分參數。

依照參數種類的不一樣。分爲封裝格式參數。視頻編碼參數。音頻編碼參數。

(1) 封裝格式參數

封裝格式參數指的是封裝格式中包含的參數。包含:

1)      輸入協議

2)      封裝格式

3)      比特率

4)      時長

5)      元數據

(2)視頻編碼參數

視頻編碼參數指的是視頻碼流中的參數。包含:

1)      輸出像素格式

2)      編碼方式

3)      幀率

4)      畫面大小

(3)音頻編碼參數

音頻編碼參數指的是音頻碼流中的參數。

包含:

1)      採樣率

2)      編碼方式

3)      聲道數

參數提取模塊的流程圖如圖5所看到的。參數提取的功能在函數ffmfc_param_global()中實現。系統經過調用av_register_all()、avformat_open_input()等一系列函數直到avcodec_open()函數完畢初始化工做。初始化完畢以後。系統調用ffmfc_param_global()完畢參數提取功能。參數提取功能完畢以後。系統循環調用函數av_read_frame()獲取每幀壓縮碼流數據。

 

圖5.參數提取模塊流程

 參數提取函數ffmfc_param_global()代碼例如如下:

//全局的,僅僅設置一次
int ffmfc_param_global(VideoState *is){
	//初始化
	CString input_protocol,input_format,wxh,decoder_name,
		decoder_type,bitrate,extention,pix_fmt,framerate,timelong,decoder_name_au,sample_rate_au,channels_au;
	float framerate_temp,timelong_temp,bitrate_temp;
	//注意:把int等類型轉換成LPCTSTR
	//CString可以直接賦值給LPCTSTR
	AVFormatContext *pFormatCtx = is->ic;
	int video_stream=is->video_stream;
	int audio_stream=is->audio_stream;
	AVCodecContext *pCodecCtx = pFormatCtx->streams[video_stream]->codec;
	AVCodecContext *pCodecCtx_au = pFormatCtx->streams[audio_stream]->codec;

	URLContext *uc=(URLContext *)pFormatCtx->pb->opaque;
	URLProtocol *up=(URLProtocol *)uc->prot;
	//輸入文件的協議----------
	input_protocol.Format("%s",up->name);
	dlg->m_formatprotocol.SetWindowText(input_protocol);

	//視頻解碼參數,有視頻的時候設置
	if(video_stream!=-1){
		wxh.Format("%d x %d",pCodecCtx->width,pCodecCtx->height);
		dlg->m_codecvresolution.SetWindowText(wxh);

		decoder_name.Format("%s",pCodecCtx->codec->long_name);
		dlg->m_codecvname.SetWindowText(decoder_name);
		//幀率顯示還有問題
		framerate_temp=(pFormatCtx->streams[video_stream]->r_frame_rate.num)/(pFormatCtx->streams[video_stream]->r_frame_rate.den);
		framerate.Format("%5.2ffps",framerate_temp);
		dlg->m_codecvframerate.SetWindowText(framerate);

		switch(pCodecCtx->pix_fmt){
		case 0:
			pix_fmt.Format("YUV420P");break;
		case 1:
			pix_fmt.Format("YUYV422");break;
		case 2:
			pix_fmt.Format("RGB24");break;
		case 3:
			pix_fmt.Format("BGR24");break;
		case 12:
			pix_fmt.Format("PIX_FMT_YUVJ420P");break;	
		default:
			pix_fmt.Format("UNKNOWN");
		}
		dlg->m_codecvpixfmt.SetWindowText(pix_fmt);
	}
	//音頻解碼參數。有音頻的時候設置
	if(audio_stream!=-1){
		decoder_name_au.Format("%s",pCodecCtx_au->codec->long_name);
		dlg->m_codecaname.SetWindowText(decoder_name_au);
		sample_rate_au.Format("%d",pCodecCtx_au->sample_rate);
		dlg->m_codecasamplerate.SetWindowText(sample_rate_au);
		channels_au.Format("%d",pCodecCtx_au->channels);
		dlg->m_codecachannels.SetWindowText(channels_au);
	}
	//顯示成以k爲單位
	bitrate_temp=((float)(pFormatCtx->bit_rate))/1000;
	bitrate.Format("%5.2fkbps",bitrate_temp);
	dlg->m_formatbitrate.SetWindowText(bitrate);
	//duration是以微秒爲單位
	timelong_temp=(pFormatCtx->duration)/1000000;
	//轉換成hh:mm:ss形式
	int tns, thh, tmm, tss;
	tns  = (pFormatCtx->duration)/1000000;
	thh  = tns / 3600;
	tmm  = (tns % 3600) / 60;
	tss  = (tns % 60);
	timelong.Format("%02d:%02d:%02d",thh,tmm,tss);
	dlg->m_formatduration.SetWindowText(timelong);
	dlg->m_duration.SetWindowText(timelong);
	//輸入文件的封裝格式------
	input_format.Format("%s",pFormatCtx->iformat->long_name);
	dlg->m_formatinputformat.SetWindowText(input_format);
	//------------------------


	//bitrate.Format("%d",pCodecCtx->bit_rate);
	//dlg->m_bitrate.SetWindowText(bitrate);

	//MetaData------------------------------------------------------------
	//從AVDictionary得到
	//需要用到AVDictionaryEntry對象
	//CString author,copyright,description;
	CString meta=NULL,key,value;
	AVDictionaryEntry *m = NULL;
	//不用一個一個找出來
	/*	m=av_dict_get(pFormatCtx->metadata,"author",m,0);
	author.Format("做者:%s",m->value);
	m=av_dict_get(pFormatCtx->metadata,"copyright",m,0);
	copyright.Format("版權:%s",m->value);
	m=av_dict_get(pFormatCtx->metadata,"description",m,0);
	description.Format("描寫敘述:%s",m->value);
	*/
	//使用循環讀出
	//(需要讀取的數據,字段名稱。前一條字段(循環時使用),參數)
	while(m=av_dict_get(pFormatCtx->metadata,"",m,AV_DICT_IGNORE_SUFFIX)){
		key.Format(m->key);
		value.Format(m->value);
		meta+=key+"\t:"+value+"\r\n" ;
	}

	//EditControl換行用\n不行。需要使用\r\n
	//除了要用\r\n外。還要都CEdit 的屬性進行設置:
	//Auto HScroll 設置爲 False
	//MultiLine  設置爲 True

	//dlg->m_metadata.SetWindowText(author+"\r\n"+copyright+"\r\n"+description);
	dlg->m_formatmetadata.SetWindowText(meta);
	//--------------------------------------------------------------------
	return 0;
}


2.4.  碼流分析模塊

碼流分析模塊在視頻播放過程當中,伴隨着視頻的解碼。分析當中的視音頻參數。

可以分爲視頻碼流分析模塊和音頻碼流分析模塊。

(1)視頻碼流分析模塊

視頻碼流分析模塊伴隨着視頻的解碼,分析每一個視頻幀的參數。包含:

1)      序號

2)      幀類型

3)      關鍵幀

4)      碼流序號

5)      PTS

(2) 音頻碼流分析模塊

音頻碼流分析模塊伴隨着音頻的解碼,分析音頻幀的參數。包含:

1)      序號

2)      大小

3)      PTS

 

碼流分析模塊的流程圖如圖6所看到的。視頻碼流分析功能在函數ffmfc_param_vframe()中實現。音頻碼流分析功能在函數ffmfc_param_aframe()中實現。

這兩個函數在系統一幀一幀解碼視頻/音頻的過程當中循環調用。系統在初始化完畢以後,調用av_read_frame()獲取一幀一幀的視頻/音頻壓縮編碼數據(存儲在結構體AVPacket中)。獲取一幀壓縮編碼數據以後,首先推斷它的類型。

假設該幀數據是視頻。則調用avcodec_decode_video2()對該幀視頻進行解碼,隨後調用ffmfc_param_vframe()分析該幀視頻的參數(主要存儲在結構體AVFrame中)。

假設該幀數據是音頻,則調用avcodec_decode_audio4()對該幀音頻進行解碼。隨後調用ffmfc_param_aframe()分析該幀音頻的參數(主要也是存儲在結構體AVFrame中)。

 

圖6.碼流分析模塊流程

 視頻碼流分析的函數ffmfc_param_vframe()代碼例如如下:

//視頻幀參數提取
int ffmfc_param_vframe(VideoState *is,AVFrame *pFrame,AVPacket *packet){
	//--------------------------------------------------------------------
	CString key_frame,pict_type,reference,f_index,pts,dts,codednum;
	AVFormatContext *pFormatCtx = is->ic;
	int video_stream=is->video_stream;
	AVCodecContext *pCodecCtx = pFormatCtx->streams[video_stream]->codec;
	//避免數據太多。超過必定量以後,就會清零--------------------------

	if(vframe_index>=MAX_FRAME_NUM){
		dlg->SystemClear();
	}

	//------------------------------
	f_index.Format("%d",vframe_index);
	//獲取當前記錄條數
	int nIndex=dlg->vddlg->m_videodecodelist.GetItemCount();
	//「行」數據結構
	LV_ITEM lvitem;
	lvitem.mask=LVIF_TEXT;
	lvitem.iItem=nIndex;
	lvitem.iSubItem=0;
	//注:vframe_index不可以直接賦值!
	//務必使用f_index運行Format!再賦值!
	lvitem.pszText=(char *)(LPCTSTR)f_index;
	//------------------------


	switch(pFrame->key_frame){
	case 0:
		key_frame.Format("No");break;
	case 1:
		key_frame.Format("Yes");break;
	default:
		key_frame.Format("Unknown");
	}

	switch(pFrame->pict_type){
	case 0:
		pict_type.Format("Unknown");break;
	case 1:
		pict_type.Format("I");break;
	case 2:
		pict_type.Format("P");break;
	case 3:
		pict_type.Format("B");break;
	case 4:
		pict_type.Format("S");break;
	case 5:
		pict_type.Format("SI");break;
	case 6:
		pict_type.Format("SP");break;
	case 7:
		pict_type.Format("BI");break;
	default:
		pict_type.Format("Unknown");
	}

	reference.Format("%d",pFrame->reference);
	pts.Format("%d",pFrame->pkt_pts);
	dts.Format("%d",pFrame->pkt_dts);
	codednum.Format("%d",pFrame->coded_picture_number);

	//插入表格------------------------
	dlg->vddlg->m_videodecodelist.InsertItem(&lvitem);
	dlg->vddlg->m_videodecodelist.SetItemText(nIndex,1,pict_type);
	dlg->vddlg->m_videodecodelist.SetItemText(nIndex,2,key_frame);
	dlg->vddlg->m_videodecodelist.SetItemText(nIndex,3,codednum);
	dlg->vddlg->m_videodecodelist.SetItemText(nIndex,4,pts);
	dlg->vddlg->m_videodecodelist.SendMessage(WM_VSCROLL, SB_BOTTOM, NULL);
	vframe_index++;
	return 0;
}


 音頻碼流分析的函數ffmfc_param_aframe()代碼例如如下:

//音頻幀參數提取
int ffmfc_param_aframe(VideoState *is,AVFrame *pFrame,AVPacket *packet){
	//--------------------------------------------------------------------
	AVFormatContext *pFormatCtx = is->ic;
	int audio_stream=is->audio_stream;
	AVCodecContext *pCodecCtx = pFormatCtx->streams[audio_stream]->codec;
	//避免數據太多,超過必定量以後,就會清零--------------------------

	if(aframe_index>=MAX_FRAME_NUM){
		dlg->SystemClear();
	}
	//------------------------------
	CString number,packet_size,dts,pts;
	//---------------
	number.Format("%d",aframe_index);
	//獲取當前記錄條數
	int nIndex=dlg->addlg->m_audiodecodelist.GetItemCount();
	//「行」數據結構
	LV_ITEM lvitem;
	lvitem.mask=LVIF_TEXT;
	lvitem.iItem=nIndex;
	lvitem.iSubItem=0;
	//注:frame_index不可以直接賦值!

//務必使用f_index運行Format!再賦值! lvitem.pszText=(char *)(LPCTSTR)number; //------------------------ packet_size.Format("%d",packet->size); pts.Format("%d",packet->pts); dts.Format("%d",packet->dts); //--------------- dlg->addlg->m_audiodecodelist.InsertItem(&lvitem); dlg->addlg->m_audiodecodelist.SetItemText(nIndex,1,packet_size); dlg->addlg->m_audiodecodelist.SetItemText(nIndex,2,pts); dlg->addlg->m_audiodecodelist.SetItemText(nIndex,3,dts); dlg->addlg->m_audiodecodelist.SendMessage(WM_VSCROLL, SB_BOTTOM, NULL); aframe_index++; return 0; }

相關文章
相關標籤/搜索