ffmpeg和SDL學習筆記

根據ffmpeg官方網站上的例子程序開始學習ffmpegSDL編程。

SDL是一個跨平臺的多媒體開發包。適用于游戲,模擬器,播放器等應用軟件開發。支持linuxwin32 等操作系統。

主要應用:

視頻

  • 設置8bpp或更高的任意色彩深度的視頻模式。如果某個模式硬件不支持,可以選擇轉化為另一模式。
  • 直接寫入線性的圖像幀緩沖(framebuffer)。
  • 用顏色鍵值(colorkey)或者alpha混合屬性創建surface
  • Surfaceblit能自動的轉化為目標格式。blit是優化過的,并能使用硬件加速。x86平臺上有針對MMX優化過的blit
  • 硬件加速的blitfill(填充)操作,如果硬件支持的話。

事件

  • 提供以下事件:
    • 應用程序的visibility發生改變
    • 鍵盤輸入
    • 鼠標輸入
    • 用戶要求的退出
  • 每種事件都能通過SDL_EventState()關閉或者打開。
  • 事件經由用戶指定的過濾函數再被加入到內部的事件隊列。
  • 線程安全的事件隊列。

音頻

  • 設置8位和16位的音頻,單聲道或者立體聲,如果格式硬件不支持,可以選擇轉換。
  • 由獨立的線程執行音頻部分,并提供用戶回調(callback)機制。
  • 設計上考慮到了客戶定制的軟混音器,但實際上在例程中就包含了一個完整的音頻/音樂輸出庫。

CD音頻

  • 完整的CD音頻控制API

線程

  • 簡單的線程創建API
  • 用于同步的簡單的二進制信號量(semaphores?

定時器

  • 讀取已流逝的毫秒數。
  • 等待指定的毫秒數。
  • 設置一個10毫秒精度的周期性定時器。

字節序無關

  • 偵測當前系統的字節序
  • 快速轉換數據的函數
  • 讀寫指定字節序的數據

這里我們使用SDL作為音視頻輸出對象,ffmpeg完成音視頻的解碼。

像使用其他軟件包或者開發庫一樣,首先肯定要初始化相應的庫,然后才能夠使用,初始化函數如下:

if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
??? ??? {
????????? fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());
????????? return -1 ;
??? ??? }

SDL有很多方法是實現視頻的輸出,但是YUV overlay是一種簡單而又常用的方法,具體使用方法是:

首先創建一個surface用來顯示視頻數據,然后創建一個overlay,然后就可以通過overlay輸出視頻到surface

其創建過程如下:

int? init_sdl(int width ,int height)
{
??

??? //create screen for displaying
??? screen = SDL_SetVideoMode(width, height, 0, 0);
??? if(!screen)
??? {
????????? fprintf(stderr, "SDL: could not set video mode - exiting/n");
????????? return -1 ;
??? }
??
??? //Now we create a YUV overlay on that screen so we can input video to it:
??? bmp = SDL_CreateYUVOverlay(width, height,
?????????????????????????? SDL_YV12_OVERLAY, screen);

??? return 0 ;
}

創建后就可以顯示視頻數據了,我對此進行了簡單的封裝,如下:

//顯示函數,提取一個完整的視頻幀后,就可以顯示此函數
void sdl_display(AVPicture *pict,SDL_Overlay *bmp,enum PixelFormat src_fmt,int width,int height)
{
??? SDL_Rect rect ;

?? struct SwsContext *img_convert_ctx=NULL;
?? AVPicture p;


?? SDL_LockYUVOverlay(bmp);

?????????????
???? p.data[0] = bmp->pixels[0];
???? p.data[1] = bmp->pixels[2];
???? p.data[2] = bmp->pixels[1];

????? p.linesize[0] = bmp->pitches[0];
????? p.linesize[1] = bmp->pitches[2];
????? p.linesize[2] = bmp->pitches[1];
?

???? //
視頻格式轉化為YUV420P格式
?????? img_convert_ctx=sws_getCachedContext(img_convert_ctx,width,height,
???????????????????? src_fmt,width,height,PIX_FMT_YUV420P,
??????????????????? SWS_X ,NULL,NULL,NULL) ;
?????? if (img_convert_ctx == NULL)
?????? {
???????????????????????
???????????????? printf("can't init convert context!/n") ;
???????????????? return ;
??????? }
????? sws_scale(img_convert_ctx, pict->data, pict->linesize,
?????????????????????????? 0,width, p.data, p.linesize);
?????
?????????????
?????? SDL_UnlockYUVOverlay(bmp);

?//設置顯示區域的位置和大小
?????? rect.x = 0;
?????? rect.y = 0;
?????? rect.w = width;
?????? rect.h = height;
??? //
顯示視頻幀
?????? SDL_DisplayYUVOverlay(bmp, &rect);
?
}

這樣在解碼出一幀數據后就可以通過調用此函數完成視頻的顯示了

視頻顯示搞定了,那么該輪到音頻輸出

要想輸出音頻,首先必須得打開音頻設備,SDL對音頻設備的打開和初始化已經做好了封裝,我們通過調用SDL_OpenAudio 來打開和初始化音頻設備,通過結構體 SDL_AudioSpec 設置相應的參數,然后將參數通過 SDL_OpenAudio 設置好設備,封裝如下:

SDL_AudioSpec audio_spec ,spec;

int init_sdl_audio(AVCodecContext *aCodecCtx)
{
??? audio_spec.freq = aCodecCtx->sample_rate;
??? audio_spec.format = AUDIO_S16SYS;
??? audio_spec.channels = aCodecCtx->channels;
??? audio_spec.silence = 0;
??? audio_spec.samples = SDL_AUDIO_BUFFER_SIZE;
??? audio_spec.callback = audio_callback;
??? audio_spec.userdata = aCodecCtx;

??? if(SDL_OpenAudio(&audio_spec, &spec) < 0)
??? {
? ??? ??? fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());
? ??? ??? return -1;
??? }

??? return 0 ;
}

其他就和視頻一樣了,先分解出音頻流,然后根據音頻流找出解碼上下文,再根據解碼上下文找到解碼器,并打開了,接著就可以進行解碼了。

但是我們不能想解碼視頻一樣,直接對音頻包進行解碼,我們不斷從文件中的packet,同時SDL又要不斷的調用回調函數,解決的辦法是創建一個互斥隊列,ffmpeg已經為我們封裝了一個AVPacketList結構體,我們需要對此進行再次封裝如下:

typedef struct PacketQueue {

?

? AVPacketList

?

?*first_pkt, *last_pkt;

?

? int nb_packets;

?

? int size;

?

? SDL_mutex

?

?*mutex;

?

? SDL_cond

?

?*cond;

?

} PacketQueue;

?

我們得注意:這里的sizepacket的大小,而nb_packets是隊列中packet的個數。

?

對于一個隊列首先得有一個初始化函數,完成初始化

?

void packet_queue_init(PacketQueue *q) {

?

? memset(q, 0, sizeof(PacketQueue));

?

? q->mutex = SDL_CreateMutex

?

();

?

? q->cond = SDL_CreateCond

?

();

?

}

?

很明顯這個初始化函數完成了隊列的內存分配、互斥量和條件量的創建。

?

然后就是入隊和出隊的函數

?

//put audio packet in the queue

?

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

?



?

? AVPacketList *pkt1;

?

//if pkt is not allocated ,allocate it

?

? if(av_dup_packet(pkt) < 0) {

?

??? return -1;

?

? }

?

//allocate space for new member of queue
pkt1 = av_malloc(sizeof(AVPacketList));

?

? if (!pkt1)

?

??? return -1;

?

//put pkt in pkt1

?

? pkt1->pkt = *pkt;

?

? pkt1->next = NULL;

?

?

?

//lock queue and wait until finishing put

?

? SDL_LockMutex(q->mutex);

?

//if last_pkt is NULL,it means that the queue is NULL,so put the packet in the first position

?

? if (!q->last_pkt)

?

??? q->first_pkt = pkt1;

?

? else

?

??? q->last_pkt->next = pkt1;

?

? q->last_pkt = pkt1;

?

? q->nb_packets++;

?

? q->size += pkt1->pkt.size;

?

//send signal of finish

?

? SDL_CondSignal(q->cond);

?

?

?

? SDL_UnlockMutex(q->mutex);

?

? return 0;

?

}

?



?

//put audio packet in the queue

?

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {

?

? AVPacketList *pkt1;

?

? int ret;

?

?

?

? SDL_LockMutex(q->mutex);

?

?

?

? for(;;) {

?

???

?

??? if(quit) {

?

????? ret = -1;

?

????? break;

?

??? }

?



?

??? pkt1 = q->first_pkt;

?

??? if (pkt1) {

?

????? q->first_pkt = pkt1->next;

?

????? if (!q->first_pkt)

?

??????? q->last_pkt = NULL;

?

????? q->nb_packets--;

?

????? q->size -= pkt1->pkt.size;

?

????? *pkt = pkt1->pkt;

?

????? av_free(pkt1);

?

????? ret = 1;

?

????? break;

?

??? } else if (!block) {

?

????? ret = 0;

?

????? break;

?

??? } else {

?

????? SDL_CondWait(q->cond, q->mutex);

?

??? }

?

? }

?

? SDL_UnlockMutex(q->mutex);

?

? return ret;

?

}

?

這里我們必須得注意SDL為音頻處理創建了一個單獨的線程,線程中通過調用回調函數完成從包中解碼出音頻幀

?

然后再調用解碼函數將音頻幀解碼出來!

?

void audio_callback(void *userdata, Uint8 *stream, int len) {

?



?

? AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;

?

? int len1, audio_size;

?



?

? static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];

?

? static unsigned int audio_buf_size = 0;

?

? static unsigned int audio_buf_index = 0;

?



?

? while(len > 0) {

?

??? if(audio_buf_index >= audio_buf_size) {
audio_size = audio_decode_frame(aCodecCtx, audio_buf,

?

????????????????????????????????????? sizeof(audio_buf));

?

????? if(audio_size < 0) {

?

???????

?

??????? audio_buf_size = 1024;

?

??????? memset(audio_buf, 0, audio_buf_size);

?

????? } else {

?

??????? audio_buf_size = audio_size;

?

????? }

?

????? audio_buf_index = 0;

?

??? }

?

??? len1 = audio_buf_size - audio_buf_index;

?

??? if(len1 > len)

?

????? len1 = len;

?

??? memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);

?

??? len -= len1;

?

?? ?stream += len1;

?

??? audio_buf_index += len1;

?

? }

?

}

?

解碼函數

?

//decode audio frame

?

int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf,

?

?????????????????????? int buf_size) {

?



?

? static AVPacket pkt;

?

? static uint8_t *audio_pkt_data = NULL;

?

? static int audio_pkt_size = 0;

?



?

? int len1, data_size;

?



?

? for(;;) {

?

??? while(audio_pkt_size > 0) {

?

????? data_size = buf_size;

?

????? len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size,

?

?????????????????????????????? ? audio_pkt_data, audio_pkt_size);

?

????? if(len1 < 0) {

?

???????

?

??????? audio_pkt_size = 0;

?

??????? break;

?

????? }

?

????? audio_pkt_data += len1;

?

????? audio_pkt_size -= len1;

?

????? if(data_size <= 0) {

?

???????

?

??????? continue;

?

????? }

?

?????

?

????? return data_size;

?

??? }

?

??? if(pkt.data)

?

????? av_free_packet(&pkt);

?



?

??? if(quit) {

?

????? return -1;

?

??? }

?



?

??? if(packet_queue_get(&audioq, &pkt, 1) < 0) {

?

????? return -1;

?

??? }

?

??? audio_pkt_data = pkt.data;

?

??? audio_pkt_size = pkt.size;

?

? }

?

}

?

//主函數

?

int main()

?

{

?

??????? AVFormatContext *pFormatCtx ;

?

??????? AVCodecContext *pCodecCtx,*aCodecCtx ;

?

??????? AVCodec *pCodec,*aCodec ;

?

??????? AVStream *st;

?

AVFrame *pFrame ;

?

??????? AVPacket packet ;

?

??????? struct SwsContext *img_convert_ctx=NULL;

?

??????? SDL_Event?????? event;

?

??????? uint8_t *buffer ;

?

??????? SDL_Rect rect ;

?

??????? char *filename="1.asf" ;

?

??????? int ret,i,videoStream,audioStream,numBytes,frameFinished ;

?



?

??????? ?//init sdl library with video and audio

?

??? ??? if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))

?

??? ??? {

?

????????? fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());

?

????????? return -1 ;

?

??? ??? }

?

//init the format and codec library ?

?

??????? av_register_all() ;

?



?

??????? ret=av_open_input_file(&pFormatCtx,filename,NULL,0,NULL) ;

?

??????? if(ret<0)

?

??????? {

?

?????????????? printf("Error1:open input file failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//retrive stream information

?

??????? ret=av_find_stream_info(pFormatCtx) ;

?

??????? if(ret<0)

?

??????? {

?

?????????????? printf("Error2:find stream information failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?



?

//dump information about file onto standard error

?

??????? dump_format(pFormatCtx,0,filename,0) ;

?



?

??????? videoStream=-1 ;

?

??????? audioStream=-1 ;

?

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

?

??????? ?{

?

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

?

???? ????????????????? &&videoStream < 0)

?

?????????????? {

?

??? ?????????????????? videoStream=i;

?

? ???????????? }

?

? ???????????? if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO &&

?

???? ????????????????? audioStream < 0)

?

?????????????? {

?

??? ?????????????????? audioStream=i;

?

? ???????????? }

?

??????? }

?

???????

?

//check whether find video stream and audio stream

?

??????? if(videoStream==-1)

?

??????? {

?

?????????????? printf("Error3:can't find video stream!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

??????? if(audioStream==-1)

?

??????? {

?

?????????????? printf("Error4:can't find audio stream!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//get video codec context?????

?

??????? st=pFormatCtx->streams[videoStream] ;

?

??????? pCodecCtx=st->codec;??

?

//get audio codec contex

?

???????

?

??????? aCodecCtx=pFormatCtx->streams[audioStream] ->codec;??

?

???????

?

//find video codec

?

??????? pCodec=avcodec_find_decoder(pCodecCtx->codec_id) ;

?

??????? if(pCodec==NULL)

?

??????? {

?

?????????????? printf("Error5:can't find video decoder!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//open video decoder

?

??????? ret=avcodec_open(pCodecCtx,pCodec) ;

?

??????? if(ret<0)

?

??????? {

?

?????????????? printf("open video decoder failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?



?

// Allocate video frame

?

? ????? pFrame=avcodec_alloc_frame();

?

//init? audio codec context

?

??????? if(init_sdl_audio(aCodecCtx)<0)

?

??????? {

?

?????????????? printf("Error6:init sdl audio failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//find audio codec ???

?

??????? aCodec=avcodec_find_decoder(aCodecCtx->codec_id) ;

?

??????? if(aCodec==NULL)

?

??????? {

?

?????????????? printf("Error7:can't find audio decoder!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//open audio decoder

?

??????? ret=avcodec_open(aCodecCtx, aCodec);

?

??????? if(ret<0)

?

??????? {

?

?????????????? printf("Error8:open audio decoder failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?



?

?//init audio packet queue

?

? ????? packet_queue_init(&audioq);

?

? ????? SDL_PauseAudio(0);

?

//init overalay

?

??????? if(init_sdl(pCodecCtx->width,pCodecCtx->height)<0)

?

??????? {

?

?????????????? printf("Error9:init sdl library failed!/n") ;

?

?????????????? return -1 ;

?

??????? }

?

//不知道為什么

?

??????? ?url_set_interrupt_cb(decode_interrupt_cb);

?

//decode video and audio frame

?

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

?

??????? {

?

?????????????? if(packet.stream_index==videoStream)

?

?????????????? {

?

//decode a frame

?

?????????????? ??????? avcodec_decode_video(pCodecCtx,pFrame,&frameFinished,packet.data,packet.size) ;

?

//finish or not ?

?

?????????????????????? if(frameFinished)

?

?????????????????????? {

?

?????????????????????????????? sdl_display((AVPicture *)pFrame,bmp,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height) ;??????

?

?????????????????????? }

?

?????????????? }else if(packet.stream_index==audioStream)

?

?????????????? {

?

?????????????????????? packet_queue_put(&audioq, &packet);

?



?

?????????????? }else

?

?????????????? {

?

?????????????????????? av_free_packet(&packet) ;

?

?????????????? }

?

?????????????? SDL_PollEvent(&event);

?

?????????????? switch(event.type)

?

?????????????? {

?

?????????????? ??? case SDL_QUIT:????

?

????????????????????????????????????? quit = 1;

?

?????????????????????? ????? ???????? SDL_Quit();

?

?????????????????????? ????? ???????? exit(0);

?

?????????????????????? ????? ???????? break;

?

?????????????? ??? default:

?

?????????????? ????? ???????? break;

?

?????????????? }

?

??????? }??????

?

// Free the YUV frame

?

??????? av_free(pFrame);

?

??????? ?

?

// Close the codec

?

??????? avcodec_close(pCodecCtx);

?

??????? ?

?

// Close the video file

?

??????? av_close_input_file(pFormatCtx);

?

??????? ?

?

??????? return 0;

?



?

}

?



?

但是這個程序沒有解決音視頻同步等問題,視頻數據顯示很快!

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

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

相關文章

百練-16年9月推免-B題-字符串判等

2743:字符串判等 查看提交統計提示提問總時間限制: 1000ms內存限制: 65536kB描述判斷兩個由大小寫字母和空格組成的字符串在忽略大小寫&#xff0c;且忽略空格后是否相等。 輸入兩行&#xff0c;每行包含一個字符串。輸出若兩個字符串相等&#xff0c;輸出YES&#xff0c;否則輸…

mysql中的內置函數

mysql內置函數列表可以從mysql官方文檔查詢&#xff0c;這里僅分類簡單介紹一些可能會用到的函數。 1 數學函數 abs(x) pi() mod(x,y) sqrt(x) ceil(x)或者ceiling(x) rand(),rand(N):返回0-1間的浮點數&#xff0c;使用不同的seed N可以獲得不同的隨機數 round(x, D)&#xff…

待整理

CNN 適合處理圖片類輸入數據&#xff0c;單獨的&#xff0c;數據之間沒有關系。 rnn則適合處理序列類數據&#xff0c;視頻幀&#xff0c;語言。 深度神經網絡一般指隱藏層大于2的神經網絡。深層網絡相對于淺層網絡&#xff0c;表達能力更強。僅有一個隱藏層的神經網絡就能擬合…

希爾伯特變換_學習筆記1-傅里葉變換1

最終目標是解微分方程。第一章首先介紹了一般意義下的傅里葉變換&#xff0c;之后逐漸將傅里葉變換的概念抽象化&#xff0c;將變換的定義域進行拓展。最后少量介紹傅里葉變換在偏微分方程中的應用。習題解答是自己寫的&#xff0c;有的不會&#xff0c;有的不知道對不對。傅里…

使用 sitemesh/decorator裝飾器裝飾jsp頁面(原理及詳細配置)

摘要&#xff1a;首先這個Decorator解釋一下這個單詞&#xff1a;“裝飾器”&#xff0c;我覺得其實可以這樣理解&#xff0c;他就像我們用到的Frame&#xff0c;他把每個頁面共有的東西提煉了出來&#xff0c;也可能我們也會用各種各樣的include標簽&#xff0c;將我們的常用頁…

FFPLAY的原理(一)

概要電影文件有很多基本的組成部分。首先&#xff0c;文件本身被稱為容器Container&#xff0c;容器的類型決定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接著&#xff0c;你有一組流&#xff0c;例如&#xff0c;你經常有的是一個音頻流和一個視頻流。&…

安卓開發 新浪微博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("請輸入要統計文件的詳細地址&#…