1. WAV(PCM)文件格式分析
WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。
RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”。
WAVE文件由文件头和数据体两大部分组成。其中文件头又分为RIFF/WAV文件标识段和声音数据格式说明段两部分。
WAVE文件各部分内容及格式见附表。
常见的声音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。
采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。
对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。
WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE文件是由样本组织而成的。
在单声道WAVE文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。
WAVE文件格式说明表
偏移地址 字节数 数据类型 内 容
文件头
00H 4 char "RIFF"标志
04H 4 long int 文件长度
08H 4 char "WAVE"标志
0CH 4 char "fmt"标志
10H 4 过渡字节(不定)
14H 2 int 格式类别(10H为PCM形式的声音数据)
16H 2 int 通道数,单声道为1,双声道为2
18H 2 int 采样率(每秒样本数),表示每个通道的播放速度,
1CH 4 long int 波形音频数据传送速率,其值为通道数×每秒数据位数×每 样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。
20H 2 int 数据块的调整数(按字节算的),其值为通道数×每样本的数据位值/8。播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整。
22H 2 每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。
24H 4 char 数据标记符"data"
28H 4 long int 语音数据的长度
PCM数据的存放方式:
样本1 样本2
8位单声道 0声道 0声道
8位立体声 0声道(左) 1声道(右) 0声道(左) 1声道(右)
16位单声道 0声道低字节 0声道高字节 0声道低字节 0声道高字节
16位立体声 0声道(左)低字节 0声道(左)高字节 1声道(右)低字节 1声道(右)高字节
WAVE文件的每个样本值包含在一个整数i中,i的长度为容纳指定样本长度所需的最小字节数。
首先存储低有效字节,表示样本幅度的位放在i的高有效位上,剩下的位置为0,这样8位和16位的PCM波形样本的数据格式如下所示。
样本大小 数据格式 最大值 最小值
8位PCM unsigned int 225 016位PCM int 32767 -32767
2. FFMPEG解复用、解码测试,保存WAV音频文件和PPM图像文件,字幕的处理与保存未做完。
代码如下:
/*************************************************************************************** Notice: Copyright (c) 2011 AVIT Corporation - All Rights Reserved**** Name: example.c**** Author: Tang Qi**** Date: 2011-08-02**** Version: 1.0**** Description: The example is use ffmepg to Demux Video Files.We can prove the result** of demux is right.**** History: 2011-08-02 Tang Qi Created***************************************************************************************/#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#include <stdio.h>#include <pthread.h>/* May be follow header file don't need */#include <stdlib.h>#include <time.h>#define MAX_SAVE_FRAME 40#define MAX_AUDIO_BUF 20000000/* 定义全局变量 */pthread_mutex_t mutex;pthread_cond_t cond;int quit = 0;/* Define DWORD and WORD */typedef unsigned long DWORD; /* 4 Bytes */typedef unsigned short WORD; /* 2 Bytes *//* The struct of wave header *//* ------------------------- */typedef struct RIFF_HEADER{char avit_szRiffID[4]; /* char = "RIFF" */DWORD avit_dwRiffSize; /* filesize - 8 */char avit_szRiffFormat[4]; /*char = "WAVE" */}RIFF_HEADER;typedef struct WAVE_FORMAT{char avit_szWaveID[4]; /* char = "fmt" */DWORD avit_dwFSize;/* 过渡字节 Ox10 */WORD avit_wFormaTag; /* 编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等 */WORD avit_wChannels; /* 声道数,单声道为1,双声道为2 */DWORD avit_dwSamplesPerSec; /* 采样频率 */DWORD avit_dwAvgBytesPerSec; /* 每秒的数据量 */WORD avit_wBlockAlign; /* 块对齐 */WORD avit_wBitsPerSample; /* WAVE文件的采样大小 *///WORD sbSize; /* PCM中忽略此值 */}WAVE_FORMAT;typedef struct DATA_BLOCK{char avit_szDataID[4]; /* char = "data" */DWORD avit_dwDataSize;/* datasize = filesize - 44 */}DATA_BLOCK;/* The struct of PacketQueue is used to save packet. *//* ------------------------------------------------- */typedef struct PacketQueue {AVPacketList *first_pkt, *last_pkt;int nb_packets;int size;} PacketQueue;/* define audio and video PacketQueue*/PacketQueue audioq;PacketQueue videoq;PacketQueue subtitleq;/* The handle of packet queue *//* -------------------------- */void packet_queue_init(PacketQueue *q){memset(q, 0, sizeof(PacketQueue));pthread_mutex_init (&mutex, NULL);//create mutexpthread_cond_init (&cond, PTHREAD_PROCESS_PRIVATE);//create cond}int packet_queue_put(PacketQueue *q, AVPacket *pkt){AVPacketList *pkt1;if(av_dup_packet(pkt) < 0) {return -1;}pkt1 = av_malloc(sizeof(AVPacketList));if (!pkt1)return -1;pkt1->pkt = *pkt;pkt1->next = NULL;pthread_mutex_lock(&mutex);if (!q->last_pkt)q->first_pkt = pkt1;elseq->last_pkt->next = pkt1;q->last_pkt = pkt1;q->nb_packets++;q->size += pkt1->pkt.size;pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);return 0;}static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){AVPacketList *pkt1;int ret;pthread_mutex_lock(&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 {pthread_cond_signal(&cond);}}pthread_mutex_unlock(&mutex);return ret;}void save_video_frame(AVFrame *pFrame, int width, int height, int iFrame){FILE *frameFile;char szFilename[32];int h;/* Create file name and open file */sprintf(szFilename, "frame%d.ppm", iFrame);frameFile=fopen(szFilename, "wb");if(frameFile==NULL)return;/* Write frameFile header */fprintf(frameFile, "P6n%d %dn255n", width, height);/* Write pixel data */for(h=0; h<height; h++)fwrite(pFrame->data[0]+h*pFrame->linesize[0], 1, width*3, frameFile);/* Close the frameFile */fclose(frameFile);return;}void video_thread(AVCodecContext *pCodecCtx){AVPacket pkt1, *packet = &pkt1;int len1, frameFlag, numBytes, i;AVFrame *pFrame, *pFrameRGB;uint8_t *buffer;printf("EC: video_thread Inn");i = 0;pFrame = avcodec_alloc_frame();/* Alloc buffer to pFrameRGB */if((pFrameRGB = avcodec_alloc_frame()) == NULL)return;/* 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));avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);for(;;) {if(packet_queue_get(&videoq, packet, 1) < 0) {break; //quit getting packets}/* Decode video frame */len1 = avcodec_decode_video2(pCodecCtx, pFrame, &frameFlag, packet);/* Did we get a video frame? */if(frameFlag) {/* Convert the image from its native format to RGB */struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,PIX_FMT_RGB24,SWS_BICUBIC, NULL,NULL,NULL);sws_scale(img_convert_ctx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrame->data,pFrameRGB->linesize);/* Save the frame to disk */if(++i <= MAX_SAVE_FRAME)save_video_frame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);}av_free_packet(packet);}av_free(buffer);av_free(pFrameRGB);av_free(pFrame);return ;}void write_wave_header(AVCodecContext *aCodecCtx, FILE *pFile, int filesize){/* Add Header into the file which is fill up pcm data,and make this file can play *//* Write header from the beginning of file */fseek(pFile, 0, SEEK_SET);RIFF_HEADER riffHeader;memcpy(riffHeader.avit_szRiffID, "RIFF", 4);// 1-4 byteriffHeader.avit_dwRiffSize = filesize - 8;memcpy(riffHeader.avit_szRiffFormat, "WAVE", 4);fwrite(&riffHeader, 1, sizeof(riffHeader), pFile);/* Write waveformat */WAVE_FORMAT waveFormat;aCodecCtx->bits_per_coded_sample = 16;/*这个值未找到,AVCodecContext中没有。需自己定义?*/memcpy(waveFormat.avit_szWaveID, "fmt ", 4);waveFormat.avit_dwFSize = 0x10;waveFormat.avit_wFormaTag = 1;waveFormat.avit_wChannels = aCodecCtx->channels;waveFormat.avit_dwSamplesPerSec = aCodecCtx-> sample_rate;/* 44100、22050、48000HZ */waveFormat.avit_wBlockAlign = (aCodecCtx->bits_per_coded_sample)/8 * waveFormat.avit_wChannels;waveFormat.avit_dwAvgBytesPerSec = waveFormat.avit_dwSamplesPerSec * waveFormat.avit_wBlockAlign;waveFormat.avit_wBitsPerSample = aCodecCtx->bits_per_coded_sample;fwrite(&waveFormat, 1, sizeof(waveFormat), pFile);printf("channels = %d, sample_rate = %d, BlockAlign = %d, BytesPerSec = %d, BitsPerSample = %dn",waveFormat.avit_wChannels,waveFormat.avit_dwSamplesPerSec,waveFormat.avit_wBlockAlign,waveFormat.avit_dwAvgBytesPerSec,waveFormat.avit_wBitsPerSample );/* Data Block */DATA_BLOCK datablock;memcpy(datablock.avit_szDataID, "data", 4);datablock.avit_dwDataSize = filesize - 44; // 41-44 byte,calculatedfwrite(&datablock, 1, sizeof(datablock), pFile);printf("EC:audio_thread(), THe len of the PCM File is %d,End of the audio_decoden", filesize);fclose(pFile);return;}/* Read packet from PakcetQueue,and decode audio data */int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size){static AVPacket pkt,pkt_temp;static uint8_t *audio_pkt_data = NULL;static int audio_pkt_size = 0;int len1, data_size;for(;;) {while(pkt_temp.size > 0) {data_size = buf_size;len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *)audio_buf, &data_size, &pkt_temp);/* avcodec_decode_audio2() too old, change to avcodec_decode_audio3() */if(len1 < 0) {/* if error, skip this frame */pkt_temp.size = 0;break;}pkt_temp.data += len1;pkt_temp.size -= len1;if(data_size <= 0) {/* No data yet, get more frames */continue;}/* We have data, return it and come back for more later */return data_size;}/* free the current packet */if(pkt.data)av_free_packet(&pkt);/*if(quit) {return -1;}*//* read next packet from audio PacketQueue */if(packet_queue_get(&audioq, &pkt, 1) < 0) {return -1;}pkt_temp.data = pkt.data;pkt_temp.size = pkt.size;}}void audio_thread(AVCodecContext *aCodecCtx){int len1, audio_size, len, ture,filesize,n;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;len = MAX_AUDIO_BUF;n = 1;/* Creat and open a File */FILE *pFile;char filename[32];sprintf(filename, "test%d.wav", n);pFile = fopen(filename, "wb");/* AT 44 bytes pointer, write data to pFile */fseek(pFile, 44, SEEK_SET);filesize = 44;//ture = 1;/* decode audio frame */while(len > 0) {if(audio_buf_index >= audio_buf_size) {/* We have already sent all our data; get more */audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));/* Save audio_buf to the PCM File. */if(audio_size> 0){fwrite(audio_buf, 1, audio_size, pFile);filesize += audio_size;printf("EC: audio_thread() THe len of the PCM File is %dn", filesize);}if(audio_size < 0) {/* If error, output silence */audio_buf_size = 1024; // arbitrary?memset(audio_buf, 0, audio_buf_size);printf("EC: audio_thread() *****Output silence *****n" );} else {audio_buf_size = audio_size;}audio_buf_index = 0;}len1 = audio_buf_size - audio_buf_index;if(len1 > len)len1 = len;printf("EC: audio_thread(), audio_size = %d, audio_buf_size = %d, audio_buf_index = %d, len = %d, len1 = %dn",audio_size, audio_buf_size, audio_buf_index, len, len1);// memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);//stream += len1;len -= len1;audio_buf_index += len1;}/* write wave header to this file*/write_wave_header(aCodecCtx, pFile, filesize);return;}void subtitle_thread(AVCodecContext *sCodecCtx){SubPicture *sp;AVPacket pkt1, *pkt = &pkt1;int len1, got_subtitle;int i;FILE *textFile;char filename[32];int text_id = 1;sprintf(filename, "subtitle%d.txt", text_id);textFile = fopen(filename, "wb");for(;;) {if (packet_queue_get(&subtitleq, pkt, 1) < 0)break;len1 = avcodec_decode_subtitle2(sCodecCtx, &sp->sub, &got_subtitle, pkt);if (got_subtitle && len1 > 0){printf("EC: subtitle_thread() sub.format = %d, len1 = %dn", sp->sub.format, len1);for(i = 0; i < sp->sub.num_rects; i++){switch(sp->sub.rects[i]->type){case SUBTITLE_TEXT:printf("EC: subtitle() Enter in save SUBTITLE_TEXTn");fwrite(sp->sub.rects[i]->text, 1, sizeof(sp->sub.rects[i]->text), textFile);break;case SUBTITLE_ASS:printf("EC: subtitle() Enter in save SUBTITLE_ASSn");break;case SUBTITLE_BITMAP:printf("EC: subtitle() Enter in save SUBTITLE_BITMAPn");break;default:printf("EC: subtitle() Enter in save SUBTITLE_NONEn");break;}}}av_free_packet(pkt);}return;}void avit_decode(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,AVCodecContext *aCodecCtx, AVCodecContext * sCodecCtx,int subtitle_flag){pthread_t audio_id;pthread_t video_id;pthread_t subtitle_id;/* Create video decode thread */if(pthread_create(&video_id, NULL, (void *)video_thread, pCodecCtx) != 0)printf("Create video thread failure!n");/* Create audio decode thread */if(pthread_create(&audio_id, NULL, (void *)audio_thread, aCodecCtx) != 0)printf("Create audio thread failure!n");/* Create subtitle decode thread */if(subtitle_flag != -1){if(pthread_create(&subtitle_id, NULL, (void *)subtitle_thread, sCodecCtx) != 0 )printf("Create subtitle thread failure!n");}return;}int main(int argc, char *argv[]){int i, videoStream, audioStream, subtitleStream, frameFinished;AVFormatContext *pFormatCtx;AVCodecContext *pCodecCtx, *aCodecCtx, *sCodecCtx;AVCodec *pCodec, *aCodec, *sCodec;AVPacket packet;if(argc < 2) {fprintf(stderr, "Usage: test <file>n");exit(1);}/* Register all formats and codecs */av_register_all();/* Init audio PacketQueue and video PacketQueue.*/packet_queue_init(&audioq);packet_queue_init(&videoq);packet_queue_init(&subtitleq)/* Open video file */if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0){printf("Couldn't open this file,Please provide another one!n");return -1; // Couldn't open this file}/* Find stream information */if(av_find_stream_info(pFormatCtx)<0){printf("Couldn't find stream information from file!n");return -1; // Couldn't find stream information}/* Dump information about file onto standard error */dump_format(pFormatCtx, 0, argv[1], 0);/* Find the first video and audio stream */videoStream = -1;audioStream = -1;subtitleStream = -1;for(i = 0; i < pFormatCtx->nb_streams; i++) {if(pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO && videoStream < 0)videoStream = i;else if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO && audioStream < 0)audioStream = i;else (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_SUBTITLE && subtitleStream < 0)subtitleStream = i;}if(videoStream == -1){printf("Couldn't fine a video stream!n");return -1;}if(audioStream == -1){printf("Couldn't find a audio stream!n");return -1;}if(subtitleStream == -1){printf("Couldn't find a subtitle Stream!n");//Don't return -1,we play this file whithout subtitle.}/* Get a pointer to the codec context for the video stream, and open ths video codec. */pCodecCtx=pFormatCtx->streams[videoStream]->codec;if((pCodec = avcodec_find_decoder(pCodecCtx->codec_id)) == NULL){printf("Unsupported pCodec, please provide another video file!n");return -1;}if(avcodec_open(pCodecCtx, pCodec) < 0){printf("Couldn't open video codec!n");return -1;}/* Get a pointer to the codec context for the audio stream, and open the audio codec. */aCodecCtx=pFormatCtx->streams[audioStream]->codec;if((aCodec = avcodec_find_decoder(aCodecCtx->codec_id)) == NULL){printf("Unsupported aCodec, please provide another video file!n");return -1;}if(avcodec_open(aCodecCtx, aCodec) <0 ){printf("Couldn't open aucio codec!n");// Don't return -1, play the video file whit silence.return -1;}/* Get a pointer to the codec context for the subtitle stream, and open the subtitle codec. */sCodecCtx = pFormatCtx->streams[subtitleStream]->codec;if((sCodec = avcodec_find_decoder(aCodecCtx->codec_id)) == NULL){prinft("Unsupperted sCodec, we will not decode subtitle stream!n");//Don't return -1, we play thie file whithout subtitle.}if(avcodec_open(sCodecCtx, sCodec) < 0){printf("Couldn't open subtitle codec!n");}/* Enter into decode function,it create decode thread */avit_decode(pFormatCtx, aCodecCtx, pCodecCtx,sCodecCtx, subtitleStream);/* Read frame into packet and put the packet into PacketQueues */while(av_read_frame(pFormatCtx, &packet)>=0) {switch(packet.stream_index){case videoStream:packet_queue_put(&videoq, &packet);break;case audioStream:packet_queue_put(&audioq, &packet);break;case subtitleStream:packet_queue_put(&subtitleq, &packet);break;default:av_free_packet(&packet);break;}}/* When read packet to the QueuePacket Finish, wait for decode finish.*//* Wait for thread finished *//* ------------------------ */sleep(60);quit = 1;// Close the codecavcodec_close(pCodecCtx);avcodec_close(aCodecCtx);avcodec_close(sCodecCtx);av_close_input_file(pFormatCtx);return 0;}
最后
以上就是虚心树叶最近收集整理的关于FFMPEG解复用、解码测试,音频保存WAV文件,视频保存为PPM图像1. WAV(PCM)文件格式分析的全部内容,更多相关FFMPEG解复用、解码测试,音频保存WAV文件,视频保存为PPM图像1.内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复