問題は、iOSデバイスに記録されている.movファイルのmoovアトムをどのように見つけて移動し、http経由でストリーミングできるようにするかです。これを行う方法は1つありますが、ファイルにエクスポートする必要があります。これにより、理論的にはファイル全体をコピーして、ストリーミングできるようになります。
これを行う他の方法はありますか?
問題は、iOSデバイスに記録されている.movファイルのmoovアトムをどのように見つけて移動し、http経由でストリーミングできるようにするかです。これを行う方法は1つありますが、ファイルにエクスポートする必要があります。これにより、理論的にはファイル全体をコピーして、ストリーミングできるようになります。
これを行う他の方法はありますか?
iOS AV Foundation フレームワークと数行の Objective-C を使用します (Android は MOV を読み取れないため、MOV から MP4 に変換することもできます):
したがって、バッファなしでこのコードを使用すると、ライブ URL からスムーズなビデオが再生されますが、ビデオをサーバーにアップロードする前に、このコードを使用してビデオを変換し、アップロード後にビデオを変換してください。そのため、ビデオは負荷なしでスナップチャットのようにビデオを再生します。
以下のフレームワークをプロジェクトに追加することを忘れないでください。
#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アトムを見つけて特定の場所に書き込むため、ビデオファイルのこれら2つの部分を取得した直後に、ビデオファイルのバイトにアクセスする必要があります。 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];
}
楽しみ!