c++视频编辑代码小结
来源:互联网 发布:淘宝新店盗图违规扣分 编辑:程序博客网 时间:2024/06/06 20:27
》视频编辑实现有以下几个方案:
ffmpeg+android/ios, 参考代码很多,跨平台。
openmax (AL编程) + android: openmx参考例子比较少,
gpuImage 开源ios框架,支持视频剪辑滤镜等功能;
参考:开源alure-1.2.tar http://kcat.strangesoft.net/index.html
demo: <a target=_blank href="http://kcat.strangesoft.net/alffmpeg.c">http://kcat.strangesoft.net/alffmpeg.c</a>
/*有一些demo参考,未测试,慎用http://kcat.strangesoft.net/alffmpeg.c * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. *//* ChangeLog: * 1 - Initial program * 2 - Changed getAVAudioData to not always grab another packet before decoding * to prevent buffering more compressed data than needed * 3 - Update to use avcodec_decode_audio3 and fix for decoders that need * aligned output pointers * 4 - Fixed bits/channels format assumption * 5 - Improve time handling * 6 - Remove use of ALUT */#include <string.h>#include <stdlib.h>#include <stdio.h>#include <signal.h>#include <assert.h>#include <AL/al.h>#include <AL/alc.h>#include <AL/alext.h>#ifndef _WIN32#include <unistd.h>#define Sleep(x) usleep((x)*1000)#else#define WIN32_LEAN_AND_MEAN#include <windows.h>#endif/* Opaque handles to files and streams. The main app doesn't need to concern * itself with the internals */typedef struct MyFile *FilePtr;typedef struct MyStream *StreamPtr;#ifndef AL_SOFT_buffer_samples/* Sample types */#define AL_BYTE 0x1400#define AL_UNSIGNED_BYTE 0x1401#define AL_SHORT 0x1402#define AL_UNSIGNED_SHORT 0x1403#define AL_INT 0x1404#define AL_UNSIGNED_INT 0x1405#define AL_FLOAT 0x1406#define AL_DOUBLE 0x1407/* Channel configurations */#define AL_MONO 0x1500#define AL_STEREO 0x1501#define AL_REAR 0x1502#define AL_QUAD 0x1503#define AL_5POINT1 0x1504 /* (WFX order) */#define AL_6POINT1 0x1505 /* (WFX order) */#define AL_7POINT1 0x1506 /* (WFX order) */#endif/**** Helper functions ****/#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>typedef struct PacketList { AVPacket pkt; struct PacketList *next;} PacketList;struct MyStream { AVCodecContext *CodecCtx; int StreamIdx; PacketList *Packets; char *DecodedData; size_t DecodedDataSize; FilePtr parent;};struct MyFile { AVFormatContext *FmtCtx; StreamPtr *Streams; size_t StreamsSize;};/* This opens a file with ffmpeg and sets up the streams' information */FilePtr openAVFile(const char *fname){ static int done = 0; FilePtr file; /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ if(!done) {av_register_all(); av_log_set_level(AV_LOG_ERROR); done = 1;} file = (FilePtr)calloc(1, sizeof(*file)); if(file && avformat_open_input(&file->FmtCtx, fname, NULL, NULL) == 0) { /* After opening, we must search for the stream information because not * all formats will have it in stream headers */ if(av_find_stream_info(file->FmtCtx) >= 0) return file; av_close_input_file(file->FmtCtx); } free(file); return NULL;}/* This closes/frees an opened file and any of its streams. Pretty self- * explanitory... */void closeAVFile(FilePtr file){ size_t i; if(!file) return; for(i = 0;i < file->StreamsSize;i++) { StreamPtr stream = file->Streams[i]; while(stream->Packets) { PacketList *self = stream->Packets; stream->Packets = self->next; av_free_packet(&self->pkt); av_free(self); } avcodec_close(stream->CodecCtx); av_free(stream->DecodedData); free(stream); } free(file->Streams); av_close_input_file(file->FmtCtx); free(file);}/* This reports certain information from the file, eg, the number of audio * streams */int getAVFileInfo(FilePtr file, int *numaudiostreams){ unsigned int i; int audiocount = 0; if(!file) return 1; for(i = 0;i < file->FmtCtx->nb_streams;i++) { if(file->FmtCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) audiocount++; } *numaudiostreams = audiocount; return 0;}/* This retrieves a handle for the given audio stream number (generally 0, but * some files can have multiple audio streams in one file) */StreamPtr getAVAudioStream(FilePtr file, int streamnum){ unsigned int i; if(!file) return NULL; for(i = 0;i < file->FmtCtx->nb_streams;i++) { if(file->FmtCtx->streams[i]->codec->codec_type != CODEC_TYPE_AUDIO) continue; if(streamnum == 0) { StreamPtr stream; AVCodec *codec; void *temp; size_t j; /* Found the requested stream. Check if a handle to this stream * already exists and return it if it does */ for(j = 0;j < file->StreamsSize;j++) { if(file->Streams[j]->StreamIdx == (int)i) return file->Streams[j]; } /* Doesn't yet exist. Now allocate a new stream object and fill in * its info */ stream = (StreamPtr)calloc(1, sizeof(*stream)); if(!stream) return NULL; stream->parent = file; stream->CodecCtx = file->FmtCtx->streams[i]->codec; stream->StreamIdx = i; /* Try to find the codec for the given codec ID, and open it */ codec = avcodec_find_decoder(stream->CodecCtx->codec_id); if(!codec || avcodec_open(stream->CodecCtx, codec) < 0) { free(stream); return NULL; } /* Allocate space for the decoded data to be stored in before it * gets passed to the app */ stream->DecodedData = av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); if(!stream->DecodedData) { avcodec_close(stream->CodecCtx); free(stream); return NULL; } /* Append the new stream object to the stream list. The original * pointer will remain valid if realloc fails, so we need to use * another pointer to watch for errors and not leak memory */ temp = realloc(file->Streams, (file->StreamsSize+1) * sizeof(*file->Streams)); if(!temp) { avcodec_close(stream->CodecCtx); av_free(stream->DecodedData); free(stream); return NULL; } file->Streams = (StreamPtr*)temp; file->Streams[file->StreamsSize++] = stream; return stream; } streamnum--; } return NULL;}/* Returns information about the given audio stream. Returns 0 on success. */int getAVAudioInfo(StreamPtr stream, ALuint *rate, ALenum *channels, ALenum *type){ if(!stream || stream->CodecCtx->codec_type != CODEC_TYPE_AUDIO) return 1; if(type) { if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) *type = AL_UNSIGNED_BYTE; else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) *type = AL_SHORT; else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S32) *type = AL_INT; else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT) *type = AL_FLOAT; else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_DBL) *type = AL_DOUBLE; else return 1; } if(channels) { if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_MONO) *channels = AL_MONO; else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) *channels = AL_STEREO; else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) *channels = AL_QUAD; else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) *channels = AL_5POINT1; else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) *channels = AL_7POINT1; else return 1; } if(rate) *rate = stream->CodecCtx->sample_rate; return 0;}/* Used by getAV*Data to search for more compressed data, and buffer it in the * correct stream. It won't buffer data for streams that the app doesn't have a * handle for. */static int getNextPacket(FilePtr file, int streamidx){ PacketList *packet = (PacketList*)av_malloc(sizeof(PacketList)); packet->next = NULL;next_pkt: while(av_read_frame(file->FmtCtx, &packet->pkt) >= 0) { StreamPtr *iter = file->Streams; StreamPtr *iter_end = iter + file->StreamsSize; /* Check each stream the user has a handle for, looking for the one * this packet belongs to */ while(iter != iter_end) { if((*iter)->StreamIdx == packet->pkt.stream_index) { PacketList **last = &(*iter)->Packets; while(*last != NULL) last = &(*last)->next; *last = packet; if((*iter)->StreamIdx == streamidx) return 1; packet = (PacketList*)av_malloc(sizeof(PacketList)); packet->next = NULL; goto next_pkt; } iter++; } /* Free the packet and look for another */ av_free_packet(&packet->pkt); } av_free(packet); return 0;}/* The "meat" function. Decodes audio and writes, at most, length bytes into * the provided data buffer. Will only return less for end-of-stream or error * conditions. Returns the number of bytes written. */int getAVAudioData(StreamPtr stream, void *data, int length){ int dec = 0; if(!stream || stream->CodecCtx->codec_type != CODEC_TYPE_AUDIO) return 0; while(dec < length) { /* If there's no decoded data, find some */ if(stream->DecodedDataSize == 0) { int size; int len; /* If there's no more input data, break and return what we have */ if(!stream->Packets && !getNextPacket(stream->parent, stream->StreamIdx)) break; /* Decode some data, and check for errors */ size = AVCODEC_MAX_AUDIO_FRAME_SIZE; while((len=avcodec_decode_audio3(stream->CodecCtx, (int16_t*)stream->DecodedData, &size, &stream->Packets->pkt)) == 0) { PacketList *self; if(size > 0) break; self = stream->Packets; stream->Packets = self->next; av_free_packet(&self->pkt); av_free(self); if(!stream->Packets) break; } if(!stream->Packets) continue; if(len < 0) break; if(len > 0) { if(len < stream->Packets->pkt.size) { /* Move the remaining data to the front and clear the end * bits */ int remaining = stream->Packets->pkt.size - len; memmove(stream->Packets->pkt.data, &stream->Packets->pkt.data[len], remaining); memset(&stream->Packets->pkt.data[remaining], 0, stream->Packets->pkt.size - remaining); stream->Packets->pkt.size -= len; } else { PacketList *self = stream->Packets; stream->Packets = self->next; av_free_packet(&self->pkt); av_free(self); } } /* Set the output buffer size */ stream->DecodedDataSize = size; } if(stream->DecodedDataSize > 0) { /* Get the amount of bytes remaining to be written, and clamp to * the amount of decoded data we have */ size_t rem = length-dec; if(rem > stream->DecodedDataSize) rem = stream->DecodedDataSize; /* Copy the data to the app's buffer and increment */ memcpy(data, stream->DecodedData, rem); data = (char*)data + rem; dec += rem; /* If there's any decoded data left, move it to the front of the * buffer for next time */ if(rem < stream->DecodedDataSize) memmove(stream->DecodedData, &stream->DecodedData[rem], stream->DecodedDataSize - rem); stream->DecodedDataSize -= rem; } } /* Return the number of bytes we were able to get */ return dec;}/**** The main app ****//* Create a simple signal handler for SIGINT so ctrl-c cleanly exits. */static volatile int quitnow = 0;static void handle_sigint(int signum){ quitnow = 1; signal(signum, SIG_DFL);}/* Some helper functions to get the name from the channel and type enums. */static const char *ChannelsName(ALenum chans){ switch(chans) { case AL_MONO: return "Mono"; case AL_STEREO: return "Stereo"; case AL_REAR: return "Rear"; case AL_QUAD: return "Quadraphonic"; case AL_5POINT1: return "5.1 Surround"; case AL_6POINT1: return "6.1 Surround"; case AL_7POINT1: return "7.1 Surround"; } return "Unknown";}static const char *TypeName(ALenum type){ switch(type) { case AL_BYTE: return "S8"; case AL_UNSIGNED_BYTE: return "U8"; case AL_SHORT: return "S16"; case AL_UNSIGNED_SHORT: return "U16"; case AL_INT: return "S32"; case AL_UNSIGNED_INT: return "U32"; case AL_FLOAT: return "Float32"; case AL_DOUBLE: return "Float64"; } return "Unknown";}/* Define the number of buffers and buffer size (in bytes) to use. 3 buffers is * a good amount (one playing, one ready to play, another being filled). 32256 * is a good length per buffer, as it fits 1, 2, 4, 6, 7, 8, 12, 14, 16, 24, * 28, and 32 bytes-per-frame sizes. */#define NUM_BUFFERS 3#define BUFFER_SIZE 32256int main(int argc, char **argv){ /* The device and context handles to play with */ ALCdevice *device; ALCcontext *ctx; /* Here are the buffers and source to play out through OpenAL with */ ALuint buffers[NUM_BUFFERS]; ALuint source; ALint state; /* This will hold the state of the source */ ALbyte *data; /* A temp data buffer for getAVAudioData to write to and pass * to OpenAL with */ int count; /* The number of bytes read from getAVAudioData */ int i; /* An iterator for looping over the filenames */ /* Print out usage if no file was specified */ if(argc < 2) { fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]); return 1; } /* Set up our signal handler to run on SIGINT (ctrl-c) */ if(signal(SIGINT, handle_sigint) == SIG_ERR) { fprintf(stderr, "Unable to set handler for SIGINT!\n"); return 1; } /* Open and initialize a device with default settings */ device = alcOpenDevice(NULL); if(!device) { fprintf(stderr, "Could not open a device!\n"); return 1; } ctx = alcCreateContext(device, NULL); if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE) { if(ctx != NULL) alcDestroyContext(ctx); alcCloseDevice(device); fprintf(stderr, "Could not set a context!\n"); return 1; } /* Generate the buffers and source */ alGenBuffers(NUM_BUFFERS, buffers); if(alGetError() != AL_NO_ERROR) { alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(device); fprintf(stderr, "Could not create buffers...\n"); return 1; } alGenSources(1, &source); if(alGetError() != AL_NO_ERROR) { alDeleteBuffers(NUM_BUFFERS, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(device); fprintf(stderr, "Could not create source...\n"); return 1; } /* Set parameters so mono sources won't distance attenuate */ alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); alSourcei(source, AL_ROLLOFF_FACTOR, 0); if(alGetError() != AL_NO_ERROR) { alDeleteSources(1, &source); alDeleteBuffers(NUM_BUFFERS, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(device); fprintf(stderr, "Could not set source parameters...\n"); return 1; } data = malloc(BUFFER_SIZE); if(data == NULL) { alDeleteSources(1, &source); alDeleteBuffers(NUM_BUFFERS, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(device); fprintf(stderr, "Could not create temp buffer...\n"); return 1; } /* Play each file listed on the command line */ for(i = 1;i < argc && !quitnow;i++) { static ALenum old_format; static ALuint old_rate; /* The base time to use when determining the playback time from the * source. */ static int64_t basetime; static int64_t filetime; /* Handles for the audio stream */ FilePtr file; StreamPtr stream; /* The format of the output stream */ ALenum format = 0; ALenum channels; ALenum type; ALuint rate; /* Open the file and get the first stream from it */ file = openAVFile(argv[i]); stream = getAVAudioStream(file, 0); if(!stream) { closeAVFile(file); fprintf(stderr, "Could not open audio in %s\n", argv[i]); continue; } /* Get the stream format, and figure out the OpenAL format. We use the * AL_EXT_MCFORMATS extension to provide output of Quad, 5.1, and 7.1 * audio streams */ if(getAVAudioInfo(stream, &rate, &channels, &type) != 0) { closeAVFile(file); fprintf(stderr, "Error getting audio info for %s\n", argv[i]); continue; } if(type == AL_UNSIGNED_BYTE) { if(channels == AL_MONO) format = AL_FORMAT_MONO8; else if(channels == AL_STEREO) format = AL_FORMAT_STEREO8; else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(channels == AL_QUAD) format = alGetEnumValue("AL_FORMAT_QUAD8"); else if(channels == AL_5POINT1) format = alGetEnumValue("AL_FORMAT_51CHN8"); else if(channels == AL_7POINT1) format = alGetEnumValue("AL_FORMAT_71CHN8"); } } else if(type == AL_SHORT) { if(channels == AL_MONO) format = AL_FORMAT_MONO16; else if(channels == AL_STEREO) format = AL_FORMAT_STEREO16; else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(channels == AL_QUAD) format = alGetEnumValue("AL_FORMAT_QUAD16"); else if(channels == AL_5POINT1) format = alGetEnumValue("AL_FORMAT_51CHN16"); else if(channels == AL_7POINT1) format = alGetEnumValue("AL_FORMAT_71CHN16"); } } else if(type == AL_FLOAT && alIsExtensionPresent("AL_EXT_FLOAT32")) { if(channels == AL_MONO) format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); else if(channels == AL_STEREO) format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32"); else if(alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(channels == AL_QUAD) format = alGetEnumValue("AL_FORMAT_QUAD32"); else if(channels == AL_5POINT1) format = alGetEnumValue("AL_FORMAT_51CHN32"); else if(channels == AL_7POINT1) format = alGetEnumValue("AL_FORMAT_71CHN32"); } } else if(type == AL_DOUBLE && alIsExtensionPresent("AL_EXT_DOUBLE")) { if(channels == AL_MONO) format = alGetEnumValue("AL_FORMAT_MONO_DOUBLE"); else if(channels == AL_STEREO) format = alGetEnumValue("AL_FORMAT_STEREO_DOUBLE"); } if(format == 0 || format == -1) { closeAVFile(file); fprintf(stderr, "Unhandled format (%s, %s) for %s", ChannelsName(channels), TypeName(type), argv[i]); continue; } /* If the format of the last file matches the current one, we can skip * the initial load and let the processing loop take over (gap-less * playback!) */ count = 1; if(format == old_format && rate == old_rate) { /* When skipping the initial load of a file (because the previous * one is using the same exact format), just remove the length of * the previous file from the base. This is so the timing will be * from the beginning of this file, which won't start playing until * the next buffer to get queued does */ basetime -= filetime; filetime = 0; } else { int j; /* Wait for the last song to finish playing */ do { Sleep(10); alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); /* Rewind the source position and clear the buffer queue */ alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); /* Reset old variables */ basetime = 0; filetime = 0; old_format = format; old_rate = rate; /* Fill and queue the buffers */ for(j = 0;j < NUM_BUFFERS;j++) { ALint size, numchans, numbits; /* Make sure we get some data to give to the buffer */ count = getAVAudioData(stream, data, BUFFER_SIZE); if(count <= 0) break; /* Buffer the data with OpenAL and queue the buffer onto the * source */ alBufferData(buffers[j], format, data, count, rate); alSourceQueueBuffers(source, 1, &buffers[j]); /* For each successful buffer queued, increment the filetime */ alGetBufferi(buffers[j], AL_SIZE, &size); alGetBufferi(buffers[j], AL_CHANNELS, &numchans); alGetBufferi(buffers[j], AL_BITS, &numbits); filetime += size / numchans * 8 / numbits; } /* Now start playback! */ alSourcePlay(source); if(alGetError() != AL_NO_ERROR) { closeAVFile(file); fprintf(stderr, "Error starting playback...\n"); continue; } } fprintf(stderr, "Playing %s (%s, %s, %dhz)\n", argv[i], TypeName(type), ChannelsName(channels), rate); while(count > 0 && !quitnow) { /* Check if any buffers on the source are finished playing */ ALint processed = 0; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); if(processed == 0) { /* All buffers are full. Check if the source is still playing. * If not, restart it, otherwise, print the time and rest */ alGetSourcei(source, AL_SOURCE_STATE, &state); if(alGetError() != AL_NO_ERROR) { fprintf(stderr, "\nError checking source state...\n"); break; } if(state != AL_PLAYING) { alSourcePlay(source); if(alGetError() != AL_NO_ERROR) { closeAVFile(file); fprintf(stderr, "\nError restarting playback...\n"); break; } } else { int64_t curtime = 0; if(basetime >= 0) { ALint offset = 0; alGetSourcei(source, AL_SAMPLE_OFFSET, &offset); curtime = basetime + offset; } fprintf(stderr, "\rTime: %ld:%05.02f", (long)(curtime/rate/60), (float)(curtime%(rate*60))/(float)rate); Sleep(10); } continue; } /* Read the next chunk of data and refill the oldest buffer */ count = getAVAudioData(stream, data, BUFFER_SIZE); if(count > 0) { ALuint buf = 0; alSourceUnqueueBuffers(source, 1, &buf); if(buf != 0) { ALint size, numchans, numbits; /* For each successfully unqueued buffer, increment the * base time. */ alGetBufferi(buf, AL_SIZE, &size); alGetBufferi(buf, AL_CHANNELS, &numchans); alGetBufferi(buf, AL_BITS, &numbits); basetime += size / numchans * 8 / numbits; alBufferData(buf, format, data, count, rate); alSourceQueueBuffers(source, 1, &buf); alGetBufferi(buf, AL_SIZE, &size); alGetBufferi(buf, AL_CHANNELS, &numchans); alGetBufferi(buf, AL_BITS, &numbits); filetime += size / numchans * 8 / numbits; } if(alGetError() != AL_NO_ERROR) { fprintf(stderr, " !!! Error buffering data !!!\n"); break; } } } fprintf(stderr, "\rTime: %ld:%05.02f\n", (long)(filetime/rate/60), (float)(filetime%(rate*60))/(float)rate); /* All done with this file. Close it and go to the next */ closeAVFile(file); } fprintf(stderr, "Done.\n"); if(!quitnow) { /* All data has been streamed in. Wait until the source stops playing it */ do { Sleep(10); alGetSourcei(source, AL_SOURCE_STATE, &state); } while(alGetError() == AL_NO_ERROR && state == AL_PLAYING); } /* All files done. Delete the source and buffers, and close OpenAL */ free(data); alDeleteSources(1, &source); alDeleteBuffers(NUM_BUFFERS, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(device); return 0;}纯android java api实现,其实底层也用的是openmax库。
》bitmapparse.cpp 定义bmp对象操作类:
支持bmp文件头、bmp头和数据的信息提取、将data封装成bmp文件。
》FFMpegMedia ffmpeg最底层实现的基类class, 针对编解码和打开生成媒体文件对象,提供了系列基本ffmpeg方法:
初始化输入/出流,文件格式fmtCtx获取,获取/复制解码器codexCtx,
分配、读、解码pkt,包保存写入文件,frame类型判断,设置输入width,h,pixFmt等,
新建初始化aud/vid stream,打开、关闭ffmpeg api等。
》FFMpegVideo/Audio.cpp 是class FFMpegMedia的子类,扩充其。
只是提供了2个方法,将基类的virtual方法重构了下,如输出文件的ofmtCtx,oCodexCtx只带vid/aud专有信息,而不是basic class的公共信息。
》demux 定义FFMPegVideo class,针对文件对象,进行demux方法。
-->继承 FFMpegMedia basic class
》FFMpegAdapter 定义了几个公共方法。
RGBR/YUV Rotate90()图片数据旋转90度,
av_create_bmp, RGBR数据buf,生成bmp文件;
video_to_picture()/ picture_to_video(): 图片和视频文件的互相生成。
》FFMpegUtil 定义了static公共方法,
视频生成n张图片,
ffmpeg注册释放api,文件中查找类型流ID,codeId Guess,打开/关闭xxCtx,
audio,查找、选择sapleRate,channelLayout方法。
》FFMpegOperation 通过调用上面的类,对音视频,图片文件进行操作;
提供了音视频分离/合并,视频生成n张图片,图片格式互转(bmp/jpg,png)方法。
其中ConvertImage方法,支持任意图片格式互转;其原理是调用FFMpegMedia 基类对输入输出PIC进行解码编码,输出cdexCtx是根据文件名后缀,调用FFMpegParseImage 子类的saveFrame重构方法,保存解码文件。
》FFMpegImage, 继承FFMPegVideo class,对图片进行读写转码操作;
重构了基类viturl 方法: 初始化/设置codexCtx, Read/SaveFrame根据img特性实现;
》
- c++视频编辑代码小结
- iOS编辑预览视频小结
- C代码优化方法小结
- C语言代码评审小结
- c语言代码评审小结
- C语言代码评审小结
- C语言代码评审小结
- 编辑视频
- 【c#】视频显示的代码
- 一些常用代码编辑工具的使用技巧小结
- 简易计算器的c语言编辑代码
- C语言学习入门(视频+代码)
- 代码编辑
- 编辑代码
- 雷横道 视频编辑软件
- 视频编辑工具
- Vegas 简单视频编辑
- 视频编辑软件
- C语言宏定义##连接符和#符的使用
- GCD使用2 详细解释 和 使用技巧
- CGroup 介绍、应用实例及原理描述
- 浅谈java泛型
- LeetCode 046 Permutations
- c++视频编辑代码小结
- Redis学习手册(Set数据类型)
- HDU 1172 猜数字
- MDK中使用printf 直接发送汉字的方法。
- jsp实现图片上传
- web 开发工具
- 使用 PowerDesigner 和 PDMReader 逆向生成 MySQL 数据字典
- lotuscript 使用Mid方法截取字符串
- Redis学习手册(Sorted-Sets数据类型)