1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
| /** 他山之石,学习为主,版权所无,翻版不究,有错无责
基于内存的格式封装测试 使用 ./a.out a.avi a.mkv
支持的: avi mkv mp4 flv ts ...
参考: http://blog.csdn.net/leixiaohua1020/article/details/25422685
log 新版本出现: Using AVStream.codec.time_base as a timebase hint to the muxer is deprecated. Set AVStream.time_base instead.
test passed!!
mp4->avi failed 出现: H.264 bitstream malformed, no startcode found, use the h264_mp4toannexb bitstream filter 解决见: http://blog.chinaunix.net/uid-11344913-id-4432752.html 官方解释: https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb
ts -> avi passed
其它: 1、自定USE_MEM,则转换后的数据在内存中,要用write将其写回到文件 如果不定义,则每一次write都会写到文件,seek也是文件操作
2、传递给ffmpeg的avio_alloc_context中的内存p和大小size,可以使用32768。 如果转换后的数据保存在内存p1,这个内存p1一定要和前面所说的p不同。因为 在自定义的write中的buf参数,就是p,所以要拷贝到其它内存。 如定义p为32768,但定义p1为50MB,可以转换50MB的视频 测试: p为32768时,需调用write 1351次 2倍大小时,调用write 679次 p越大,调用次数最少,内存消耗越大 (用time测试,时间上没什么变化,前者为4.7s,后者为4.6s)
3、优化: 转换功能接口封装为类,把write、seek等和内存有关的操作放到类外部实现, 再传递到该类中,该类没有内存管理更好一些。 */
#include <stdio.h> #include <stdlib.h> #include <unistd.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" }
#ifndef min #define min(a,b) ((a) > (b) ? (b) : (a)) #endif
#define _LL_DEBUG_
// low level debug #ifdef _LL_DEBUG_ #define debug(fmt, ...) printf(fmt, ##__VA_ARGS__) #define LL_DEBUG(fmt, ...) printf("[DEBUG %s().%d @ %s]: " fmt, \ __func__, __LINE__, P_SRC, ##__VA_ARGS__) #else #define debug(fmt, ...) #define LL_DEBUG(fmt, ...) #endif
// 自定义的IO全部在内存 #define USE_MEM
#define DEFAULT_MEM (10*1024*1024)
#define IO_BUFFER_SIZE (32768*1)
static char g_ptr1[IO_BUFFER_SIZE] = {0};
static char* g_ptr = NULL; //static char g_ptr[DEFAULT_MEM] = {0}; static int g_nPos = 0; static int g_nTotalLen = DEFAULT_MEM;
static int g_nRealLen = 0;
static int g_fd = 0;
int my_read(void *opaque, unsigned char *buf, int buf_size) { // no return 0; }
int my_write(void *opaque, unsigned char *buf, int size) { if (g_nPos + size > g_nTotalLen) { // 重新申请 // 根据数值逐步加大 int nTotalLen = g_nTotalLen*sizeof(char) * 3 / 2; char* ptr = (char*)av_realloc(g_ptr, nTotalLen); if (ptr == NULL) { // if (g_ptr) av_free(g_ptr); return -1; } debug("org ptr: %p new ptr: %p size: %d(%0.fMB) ", g_ptr, ptr, nTotalLen, nTotalLen/1024.0/1024.0); g_nTotalLen = nTotalLen; g_ptr = ptr; debug(" realloc!!!!!!!!!!!!!!!!!!!!!!!\n"); // todo //memcpy(g_ptr + g_nPos, buf, size); } memcpy(g_ptr + g_nPos, buf, size);
if (g_nPos + size >= g_nRealLen) g_nRealLen += size; static int cnt = 1; debug("%d write %p %p pos: %d len: %d\n", cnt++, g_ptr, buf, g_nPos, size); //dump("org ", (char*)buf, 32); //dump("g_ptr ", g_ptr, 32); g_nPos += size; return 0; }
int64_t my_seek(void *opaque, int64_t offset, int whence) { int64_t new_pos = 0; // 可以为负数 int64_t fake_pos = 0;
switch (whence) { case SEEK_SET: new_pos = offset; break; case SEEK_CUR: new_pos = g_nPos + offset; break; case SEEK_END: // 此处可能有问题 new_pos = g_nTotalLen + offset; break; default: return -1; } fake_pos = min(new_pos, g_nTotalLen); if (fake_pos != g_nPos) { g_nPos = fake_pos; } debug("seek pos: %d(%d)\n", offset, g_nPos); return new_pos; }
int my_write1(void *opaque, unsigned char *buf, int size) { debug("write %p len: %d\n", buf, size); //dump("org ", (char*)buf, 32); //dump("g_ptr ", (char*)g_ptr, 32); return write(g_fd, buf, size); }
int64_t my_seek1(void *opaque, int64_t offset, int whence) { debug("seek pos: %d whence: %d\n", offset, whence); return lseek(g_fd, offset, whence); }
// 定义2个函数指针,免得后面改 static int (*write_packet)(void *opaque, uint8_t *buf, int buf_size) = #ifdef USE_MEM my_write #else my_write1 #endif ;
static int64_t (*seek)(void *opaque, int64_t offset, int whence) = #ifdef USE_MEM my_seek #else my_seek1 #endif ;
int remuxer_mem(int argc, char* argv[]) { //输入对应一个AVFormatContext,输出对应一个AVFormatContext //(Input AVFormatContext and Output AVFormatContext) AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; AVPacket pkt; const char *in_filename, *out_filename; int ret = 0; AVIOContext *avio_out = NULL;
if (argc < 3) { printf("usage: %s [input file] [output file]\n", argv[0]); printf("eg %s foo.avi bar.ts\n", argv[0]); return -1; }
in_filename = argv[1]; out_filename = argv[2];
// 分配空间 g_ptr = (char*)av_realloc(NULL, DEFAULT_MEM*sizeof(char)); // new if (g_ptr == NULL) { debug("alloc mem failed.\n"); return -1; } printf("------alloc ptr: %p\n", g_ptr); memset(g_ptr, '\0', DEFAULT_MEM);
av_register_all();
//输入(Input) if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) { printf( "Could not open input file '%s': %s\n", in_filename, strerror(errno)); goto end; } if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { printf( "Failed to retrieve input stream information\n"); goto end; }
printf("input format:\n"); av_dump_format(ifmt_ctx, 0, in_filename, 0);
//输出(Output) // 先打开要保存的文件 g_fd = open(out_filename, O_CREAT|O_RDWR, 0666); // 分配自定义的AVIOContext // 注意,参考file协议的内存,使用大小32768,而g_ptr是不同一片内存的 avio_out =avio_alloc_context((unsigned char *)g_ptr1, IO_BUFFER_SIZE, 1, NULL, NULL, write_packet, seek); if (!avio_out) { printf( "avio_alloc_context failed\n"); ret = AVERROR_UNKNOWN; goto end; } // 分配AVFormatContext // out_filename 根据输出文件扩展名来判断格式 // 注意该函数会分配AVOutputFormat avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); if (!ofmt_ctx) { printf( "Could not create output context\n"); ret = AVERROR_UNKNOWN; goto end; } ofmt_ctx->pb=avio_out; // 赋值自定义的IO结构体 ofmt_ctx->flags=AVFMT_FLAG_CUSTOM_IO; // 指定为自定义
debug("guess format: %s(%s) flag: %d\n", ofmt_ctx->oformat->name, ofmt_ctx->oformat->long_name, ofmt_ctx->oformat->flags);
for (int i = 0; i < (int)ifmt_ctx->nb_streams; i++) { //根据输入流创建输出流(Create output AVStream according to input AVStream) AVStream *in_stream = ifmt_ctx->streams[i]; AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec); if (!out_stream) { printf( "Failed allocating output stream\n"); ret = AVERROR_UNKNOWN; goto end; } //复制AVCodecContext的设置(Copy the settings of AVCodecContext) ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { printf( "Failed to copy context from input to output stream codec context\n"); goto end; } out_stream->codec->codec_tag = 0; if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } //输出一下格式------------------ printf("output format:\n"); av_dump_format(ofmt_ctx, 0, out_filename, 1);
//打开输出文件(Open output file) // !!!! 存在于内存,不需要明确指定打开,这种形式是针对文件或网络协议的 /* if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); if (ret < 0) { printf( "Could not open output file '%s': %s\n", out_filename, strerror(errno)); goto end; } } */
//写文件头(Write file header) ret = avformat_write_header(ofmt_ctx, NULL); if (ret < 0) { printf( "Error occurred when opening output file\n"); goto end; }
while (1) { AVStream *in_stream, *out_stream; //获取一个AVPacket(Get an AVPacket) ret = av_read_frame(ifmt_ctx, &pkt); if (ret < 0) break; in_stream = ifmt_ctx->streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index];
/* copy packet */ //转换PTS/DTS(Convert PTS/DTS) pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); pkt.pos = -1;
//写入(Write) ret = av_interleaved_write_frame(ofmt_ctx, &pkt); if (ret < 0) { printf( "Error muxing packet\n"); break; } av_free_packet(&pkt); }
//写文件尾(Write file trailer) av_write_trailer(ofmt_ctx);
end: avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
#ifdef USE_MEM write(g_fd, g_ptr, g_nRealLen); #endif av_freep(&avio_out); av_freep(&g_ptr);
close(g_fd); return ret; }
|