我是靠谱客的博主 虚心树叶,最近开发中收集的这篇文章主要介绍FFMPEG解复用、解码测试,音频保存WAV文件,视频保存为PPM图像1. WAV(PCM)文件格式分析,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
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 mutex
pthread_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;
else
q->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 byte
riffHeader.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,calculated
fwrite(&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 codec
avcodec_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. WAV(PCM)文件格式分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复