问题是您如何找到并移动记录在 iOS 设备上的 .mov 文件的 moov 原子,以便您可以通过 http 流式传输它。有一种方法可以做到这一点,但这需要将其导出到文件中,理论上这会使您复制整个文件,然后您就可以流式传输它。
有没有其他方法可以做到这一点?
问题是您如何找到并移动记录在 iOS 设备上的 .mov 文件的 moov 原子,以便您可以通过 http 流式传输它。有一种方法可以做到这一点,但这需要将其导出到文件中,理论上这会使您复制整个文件,然后您就可以流式传输它。
有没有其他方法可以做到这一点?
使用 iOS AV Foundation 框架和几行 Objective-C(您也可以从 MOV 转换为 MP4,因为 Android 无法读取 MOV):
因此,使用没有缓冲区的此代码可以从 Live URL 播放流畅的视频,但在将视频上传到您的服务器之前,请使用此代码并转换您的视频并在上传之后。所以视频是在没有任何负载的情况下播放视频,如 snapchat。
不要忘记将以下框架添加到您的项目中。
#import <AVFoundation/AVAsset.h> #import <AVFoundation/AVAssetExportSession.h> #import <AVFoundation/AVMediaFormat.h>
+ (void) convertVideoToMP4AndFixMooV: (NSString*)filename toPath:(NSString*)outputPath {
NSURL *url = [NSURL fileURLWithPath:filename];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:avAsset
presetName:AVAssetExportPresetPassthrough];
exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
exportSession.outputFileType = AVFileTypeAppleM4V;
// This should move the moov atom before the mdat atom,
// hence allow playback before the entire file is downloaded
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:
^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {}
else if (AVAssetExportSessionStatusFailed == exportSession.status) {
NSLog(@"AVAssetExportSessionStatusFailed");
}
else
{
NSLog(@"Export Session Status: %d", exportSession.status);
}
}];
}
这是我编辑和编写的方法的代码,用于与 iOS 一起查找然后将 moov atom 写入特定位置,因此在您拥有视频文件的这两个部分之后,您只需要访问视频文件的字节FTYP 原子从哪里结束(查看标记为 start_offset 和 last_offset 的变量),然后将发生流式传输。
- (NSString *)fixForFastPlayback:(char*)dest:(ALAsset*)selected
{
FILE *infile = NULL;
FILE *outfile = NULL;
uint32_t atom_type = 0;
uint64_t atom_size = 0;
uint64_t atom_offset = 0;
uint64_t last_offset;
uint64_t moov_atom_size;
uint64_t ftyp_atom_size = 0;
uint64_t i, j;
uint32_t offset_count;
uint64_t current_offset;
uint64_t start_offset = 0;
ALAssetRepresentation * rep = [[selected defaultRepresentation] retain];
int bufferSize = 8192; // or use 8192 size as read from other posts
int read = 0;
NSError * err = nil;
uint8_t * buffer = calloc(bufferSize, sizeof(*buffer));
uint8_t * ftyp_atom;
/* traverse through the atoms in the file to make sure that 'moov' is
* at the end */
int asset_offset = 0;
while (asset_offset < [rep size])
{
read = [rep getBytes:buffer
fromOffset:asset_offset
length:ATOM_PREAMBLE_SIZE
error:&err];
asset_offset += read;
if (err != nil)
{
NSLog(@"Error: %@ %@", err, [err userInfo]);
}
atom_size = (uint32_t)BE_32(&buffer[0]);
atom_type = BE_32(&buffer[4]);
/* keep ftyp atom */
if (atom_type == FTYP_ATOM) //no idea what an atom is, maybe a header or some sort of meta data or a file marker
{
ftyp_atom_size = atom_size;
ftyp_atom = calloc(ftyp_atom_size, sizeof(*buffer));
if (!ftyp_atom)
{
printf ("could not allocate %"PRIu64" byte for ftyp atom\n",
atom_size);
}
asset_offset -= ATOM_PREAMBLE_SIZE;
read = [rep getBytes:ftyp_atom
fromOffset:asset_offset
length:ftyp_atom_size
error:&err];
asset_offset += read;
start_offset = asset_offset;
}
else
{
asset_offset += (atom_size - ATOM_PREAMBLE_SIZE);
}
printf("%c%c%c%c %10"PRIu64" %"PRIu64"\n",
(atom_type >> 24) & 255,
(atom_type >> 16) & 255,
(atom_type >> 8) & 255,
(atom_type >> 0) & 255,
atom_offset,
atom_size);
if ((atom_type != FREE_ATOM) &&
(atom_type != JUNK_ATOM) &&
(atom_type != MDAT_ATOM) &&
(atom_type != MOOV_ATOM) &&
(atom_type != PNOT_ATOM) &&
(atom_type != SKIP_ATOM) &&
(atom_type != WIDE_ATOM) &&
(atom_type != PICT_ATOM) &&
(atom_type != UUID_ATOM) &&
(atom_type != FTYP_ATOM))
{
printf ("encountered non-QT top-level atom (is this a Quicktime file?)\n");
break;
}
atom_offset += atom_size;
/* The atom header is 8 (or 16 bytes), if the atom size (which
* includes these 8 or 16 bytes) is less than that, we won't be
* able to continue scanning sensibly after this atom, so break. */
if (atom_size < 8)
break;
}
if (atom_type != MOOV_ATOM)
{
printf ("last atom in file was not a moov atom\n");
free(ftyp_atom);
fclose(infile);
return 0;
}
asset_offset = [rep size];
asset_offset -= atom_size;
last_offset = asset_offset;
moov_atom_size = atom_size;
uint8_t * moov_atom = calloc(moov_atom_size, sizeof(*buffer));
if (!moov_atom)
{
printf ("could not allocate %"PRIu64" byte for moov atom\n",
atom_size);
}
read = [rep getBytes:moov_atom
fromOffset:asset_offset
length:moov_atom_size
error:&err];
asset_offset += read;
/* this utility does not support compressed atoms yet, so disqualify
* files with compressed QT atoms */
if (BE_32(&moov_atom[12]) == CMOV_ATOM)
{
printf ("this utility does not support compressed moov atoms yet\n");
}
/* crawl through the moov chunk in search of stco or co64 atoms */
for (i = 4; i < moov_atom_size - 4; i++)
{
atom_type = BE_32(&moov_atom[i]);
if (atom_type == STCO_ATOM)
{
printf (" patching stco atom...\n");
atom_size = BE_32(&moov_atom[i - 4]);
if (i + atom_size - 4 > moov_atom_size)
{
printf (" bad atom size\n");
}
offset_count = BE_32(&moov_atom[i + 8]);
for (j = 0; j < offset_count; j++)
{
current_offset = BE_32(&moov_atom[i + 12 + j * 4]);
current_offset += moov_atom_size;
moov_atom[i + 12 + j * 4 + 0] = (current_offset >> 24) & 0xFF;
moov_atom[i + 12 + j * 4 + 1] = (current_offset >> 16) & 0xFF;
moov_atom[i + 12 + j * 4 + 2] = (current_offset >> 8) & 0xFF;
moov_atom[i + 12 + j * 4 + 3] = (current_offset >> 0) & 0xFF;
}
i += atom_size - 4;
}
else if (atom_type == CO64_ATOM)
{
printf (" patching co64 atom...\n");
atom_size = BE_32(&moov_atom[i - 4]);
if (i + atom_size - 4 > moov_atom_size)
{
printf (" bad atom size\n");
}
offset_count = BE_32(&moov_atom[i + 8]);
for (j = 0; j < offset_count; j++)
{
current_offset = BE_64(&moov_atom[i + 12 + j * 8]);
current_offset += moov_atom_size;
moov_atom[i + 12 + j * 8 + 0] = (current_offset >> 56) & 0xFF;
moov_atom[i + 12 + j * 8 + 1] = (current_offset >> 48) & 0xFF;
moov_atom[i + 12 + j * 8 + 2] = (current_offset >> 40) & 0xFF;
moov_atom[i + 12 + j * 8 + 3] = (current_offset >> 32) & 0xFF;
moov_atom[i + 12 + j * 8 + 4] = (current_offset >> 24) & 0xFF;
moov_atom[i + 12 + j * 8 + 5] = (current_offset >> 16) & 0xFF;
moov_atom[i + 12 + j * 8 + 6] = (current_offset >> 8) & 0xFF;
moov_atom[i + 12 + j * 8 + 7] = (current_offset >> 0) & 0xFF;
}
i += atom_size - 4;
}
}
outfile = fopen(dest, "wb");
NSLog(@"%llu",last_offset);
//global variables to be used when returning the actual data
start_offset_not_c = start_offset;
last_offset_not_c = last_offset;
if (ftyp_atom_size > 0)
{
printf ("writing ftyp atom...\n");
if (fwrite(ftyp_atom, ftyp_atom_size, 1, outfile) != 1)
{
perror(dest);
}
}
printf ("writing moov atom...\n");
if (fwrite(moov_atom, moov_atom_size, 1, outfile) != 1)
{
perror(dest);
}
fclose(outfile);
free(ftyp_atom);
free(moov_atom);
ftyp_atom = NULL;
moov_atom = NULL;
return [NSString stringWithCString:dest encoding:NSStringEncodingConversionAllowLossy];
}
享受!